COSC264-Python代写
时间:2023-08-22
COSC264 Assignment
Dept. of Computer Science and Software Engineering
University of Canterbury
July 24, 2023
1 Administrivia
This assignment is part of the COSC264 assessment process. It is worth 10% of the final marks. It centers around
socket programming using the Python programming language. Your program should be a command line program
that can run from a Linux terminal.
This may seem like a daunting task, so a good place to start is the socket programming slide set. You may need to
investigate some aspects of the Python socket API yourself, so don’t forget to read the Python documentation.
Please use the discussion forum on the Learn platform for raising any unclear technical issues. Please do not send
emails with technical questions directly to the lecturers or the tutors; instead use the Learn forum. This way other
people can benefit from the question (and the answer).
Another piece of advice: socket programming is systems programming, and there can sometimes be subtle differ-
ences in the precise operation of socket calls between different (versions of) operating systems, even in a cross-
platform API like Python’s socket API. We strongly suggest that you test your programs under Linux before
submitting, as this is the OS that we will use to mark your submission.
2 Plagiarism Warning
Your submissions are logged and originality detection software will be used to compare your solution with other
solutions. Dishonest practice, which includes
ˆ letting another person or a tool like ChatGPT create all or part of an item of work,
ˆ copying all or part of an item of work from another person or a tool like ChatGPT with or without modifi-
cation, and
ˆ allowing someone else to copy all or part of an item of work,
may lead to partial or total loss of marks, no grade being awarded, the failing grade ’X’ (dishonesty) being awarded,
or other serious consequences including notification of the University Proctor.
You are encouraged to discuss the general aspects of a problem with others. However, anything you submit for
credit must be entirely your own work and not copied, with or without modification, from any other person or tool
like ChatGPT.
If you need help with specific details relating to your work, or are not sure what you are allowed to do, ask your
tutor or lecturer for advice.
If you copy someone else’s work or use a tool like ChatGPT or share details of your work with anybody else, you are
likely to be in breach of university regulations and the Computer Science and Software Engineering department’s
policy. For further information please see
ˆ Academic Integrity Guidance for Staff and Students
https://www.canterbury.ac.nz/about/governance/ucpolicy/general/academic-integrity-guidance-for-staff-and-students/
ˆ Academic Misconduct Regulations
https://www.canterbury.ac.nz/regulations/general-regulations/academic-misconduct-regulations/
1
Important note: Any code that a student develops for the assignment must not be published or otherwise made
available until after the final exam. If a student does this nonetheless, we will apply the same penalties to them
as we do to students copying code.
You will have to make a plagiarism declaration upon submission of your assignment.
3 Problem Description
Your task is to write two programs in Python. The first one, called server, will allow the other program, called
client, to send and read messages to/from other clients. The client and the server will communicate through
stream / TCP sockets, exchanging both control and actual message data.
3.1 Server
The server is a command line application and will operate as a TCP server. It will accept one parameter, to be
read from the command line (see Section 3.4 for how to use command line arguments). This parameter is the
port number to which the server will bind the socket on which it accepts incoming message requests. The port
number should be between 1,024 and 64,000 (inclusive). If it is not, then the server should print an appropriate
error message and exit. Otherwise, the server will perform the following steps:
ˆ The server creates a socket and attempts to bind it to the port number given on the command line. (In your
bind() call, it is probably a good idea to use the "0.0.0.0" special address as the IP address to bind the
socket to.) If this does not work, then the server should print an appropriate error message and exit.
ˆ The server then calls listen() on the socket. If this does not work, then the server should print an
appropriate error message, close the socket, and exit.
ˆ The server enters an infinite loop, in which the server performs the following steps:
– It first accept()s a new incoming connection (recall that accept() returns a new socket for that
connection). For logging purposes, the server prints a message which indicates the IP address and port
number of the client from which the incoming connection originated.
– It then attempts to read a MessageRequest record from the connection and checks the validity of that
MessageRequest. This process is described in Section 3.3.
– If the MessageRequest record is not valid, or if the required number of bytes for the complete request
cannot be read from the socket within a maximum of one second after accept() has returned with a
socket (see Section 3.5), then the server prints an appropriate error message, closes the socket obtained
from accept(), and goes back to the start of the loop.
– If the MessageRequest record is correct, the server processes it depending on the type of request:
* For create requests, the server will store the message text and sender name under the receiver’s
name (note this storage does not have to be persistent; storing them in a Python object will suffice).
The server will then close the socket obtained from accept(), print an informational message with
the names of the sender and receiver, and go back to the start of the loop. No response is sent back
to the client in this case.
* For read requests, the server will send a MessageResponse record back to the client that contains
all the messages that the server has for the requesting user (up to 255, on a first-in, first-out basis).
The sent messages should be removed from the server’s storage. If there are more than 255 messages
for a user, then this is noted in the MoreMsgs field of the MessageResponse record (see Section 3.3).
Once all the messages have been sent, the server should close the socket obtained from accept(),
print an informational message regarding the number of messages sent, and go back to the start of
the loop.
3.2 Client
The client is a command line application and will operate as a TCP client. The client will accept four parameters,
to be read from the command line:
ˆ The first parameter is either a string giving an IP address in dotted-decimal notation (e.g. “130.66.22.212”),
or it is the hostname of the computer running the server (e.g. “fileserver.mydomain.nz”). Either way, the
client will attempt to convert this parameter to an IP address using the getaddrinfo() function. If this
2
conversion fails (e.g. because the hostname does not exist or an IP address given in dotted-decimal notation
is not well-formed), then the client should print an appropriate error message and exit.
ˆ The second parameter is the port number to use on the server. The port number should be between 1,024
and 64,000 (inclusive). If it is not, then the client should print an appropriate error message and exit.
ˆ The third parameter is the name of the client’s user, i.e. the user whose messages are to be retrieved, or the
name of the sender of the message that is to be created. (You may assume that user names are unique and
do not need to check or enforce this.) User names must be at least 1 character long, and no longer than 255
bytes (see 3.6 for information about Python strings and bytes). If it is not, then the client should print an
appropriate error message and exit.
ˆ The fourth parameter is the type of request the client is making to the server. This should be either "read"
or "create". If it is not, then the client should print an appropriate error message and exit.
If there are fewer or more than four parameters on the command line, the client prints an appropriate error message
and exits. If the parameters are all okay, the client will go through the following steps:
ˆ The client prepares a MessageRequest. Section 3.3 describes the required format. For a create request, the
client must elicit additional information from the user; see the create request additional steps below.
ˆ The client creates a socket and connect()s with the server, using the IP address and port number inferred
from the command line.
ˆ The client sends the MessageRequest to the server over the socket and prints an appropriate informational
message.
ˆ For a read request, the client then attempts to read and convey the server’s response - see the read request
additional steps below.
ˆ Finally, the client closes the socket and exits.
If any of these steps do not succeed, the client should print an appropriate error message, close the socket (if open),
and exit without attempting the remaining steps.
3.2.1 create request additional steps
For a create request, the client requires additional information in order to prepare the MessageRequest. It should
prompt the user for the following information interactively:
ˆ The name of the receiver: This must be at least 1 character and less than 255 bytes. If it is not, then the
client prints an appropriate error message and prompts again until a valid receiver name is provided.
ˆ The message contents: The message must be at least 1 character and less than 65,535 bytes. If it is not,
then the client prints an appropriate error message and prompts again until a valid message is provided.
3.2.2 read request additional steps
For a read request, after sending the MessageRequest, the client attempts to read a MessageResponse record
from the server. See Section 3.3 for a full description of this process.
If the MessageResponse record is valid:
ˆ If the MessageResponse record indicates that there are no messages, then the client prints an appropriate
informational message, closes the socket, and exits.
ˆ Otherwise, the client attempts to read and print each message’s sender and text. If the MessageResponse
record’s MoreMsgs field indicates that there are more messages to be read from the server, the client will
print an appropriate message. Either way, the client will close the socket and exit.
If the MessageResponse record is not valid, or if there is a gap of more than one second while reading any part
of the MessageResponse record (see Section 3.5), then the client should print an appropriate error message, close
the socket, and exit.
3.3 Record Formats
All records should be created using bytearrays.
3
3.3.1 The MessageRequest Record
The format of the MessageRequest record is shown in the following figure:
It consists of the following fields:
ˆ The first 16-bit field ‘MagicNo’ is taken up by a magic number, which needs to have the value 0xAE73 in
network byte order. This is a simple safeguard to check whether the received data could actually be a
MessageRequest record.
ˆ The next 8-bit field ‘ID’ must contain either ‘1’ for a read request or ‘2’ for a create request
ˆ The next two 8-bit fields ‘NameLen’ and ‘ReceiverLen’ contain the length of the user’s name and receiver’s
name (relevant only for create requests) in bytes.
ˆ The next 16-bit field ‘MessageLen’ contains the length of the message in bytes. As this is a 2-byte field, it is
given network byte order.
ˆ Finally, the record contains NameLen + ReceiverLen +MessageLen bytes storing first the user’s name,
then (if applicable) the receiver’s name and the message itself, all in bytes, and in that order.
We refer to the first seven bytes of the record (comprising the ‘MagicNo’, ‘ID’, ‘NameLen’, ‘ReceiverLen’, and
‘MessageLen’ fields) as the fixed header.
The MessageRequest record is sent from the client to the server. To receive and process a MessageRequest record,
the server performs the following steps:
ˆ First the server attempts to read the seven bytes of the fixed header. If that is not possible without gap (see
3.5), then the server concludes that the received MessageRequest is erroneous and performs error processing
as described in Section 3.1 (i.e. printing an appropriate error message on the terminal, closing the socket
obtained from accept(), and going back to the start of the loop). Otherwise, the server performs the
following checks:
– The contents of the ‘MagicNo’ field must equal 0xAE73.
– The contents of the ‘ID’ field must be either 1 or 2.
– The contents of the ‘NameLen’ field must be at least 1.
– The contents of the ‘ReceiverLen’ field must be 0 if ‘ID’ is 1 (read request), or at least 1 if ‘ID’ is 2
(create request).
– Similarly, the ‘MessageLen’ field must be 0 if ‘ID’ is 1, or at least 1 if ‘Type’ is 2.
If any of these conditions are not met, then the server concludes that the received MessageRequest is
erroneous and performs error processing.
ˆ Then the server attempts to read exactly NameLen + ReceiverLen +MessageLen further bytes from the
MessageRequest record. If reading from the socket is not possible without gap or the server reads fewer
or more than the expected number of bytes, then the server concludes that the received MessageRequest is
erroneous and performs error processing.
4
3.3.2 The MessageResponse Record
The format of the MessageResponse record is shown in the following figure:
It consists of the following fields:
ˆ The first 16-bit field ‘MagicNo’ is taken up by a magic number, which needs to have the value 0xAE73 in
network byte order. This is a simple safeguard to check whether the received data could actually be a
MessageResponse record.
ˆ The next 8-bit field ‘ID’ must contain the fixed value ‘3’ for a MessageResponse record.
ˆ The next 8-bit field ‘NumItems’ contains the number of messages that will follow (if any).
ˆ The next 8-bit field ‘MoreMsgs’ contains either ‘0’ if there are no more messages for the user on the server,
or ‘1’ if there are more messages (i.e. if the server could not send all the messages it had stored for the user
because there were more than 255).
ˆ Next, if there are any messages, each will contain the following three fields:
– First, an 8-bit field ‘SenderLen’ contains the length of the sender’s name in bytes.
– Second, a 16-bit field ‘MessageLen’ contains the length of the message in bytes in network byte order.
– Finally, the next SenderLen+MessageLen bytes contain the sender’s name, followed by the message
text, all in bytes.
ˆ The record will repeat the previous three fields for each message contained in the record.
We refer to the first five bytes of the MessageResponse record (comprising the ‘MagicNo’, ‘ID’, and ‘NumItems’)
as the fixed header and the first three bytes of each message (comprising the ‘SenderLen’ and ‘MessageLen’)
as the message header. The MessageResponse record is sent from the server to the client in response to a
MessageRequest record. To receive and process a MessageResponse record, the client performs the following
steps:
ˆ First the client attempts to read the five bytes of the fixed header. If that is not possible without gap (see
Section 3.5), then the client concludes that the received MessageResponse is erroneous and performs error
processing as described in Section 3.2 (i.e. printing an error message on the terminal, closing the socket, and
exiting). Otherwise, the client performs the following checks:
– The contents of the ‘MagicNo’ field must equal 0xAE73.
– The contents of the ‘ID’ field must equal 3.
– The contents of the ‘MoreMsgs’ field must be either ‘0’ or ‘1’
If any of these conditions is not met, then the client concludes that the received MessageResponse is erroneous
and performs error processing.
ˆ Then, for as many messages as denoted by the ‘NumItems’ field, the client performs the following steps:
5
– The client first attempts to read the three bytes of the message header, again without gap.
– The ‘SenderLen’ and ‘MessageLen’ fields must be at least 1. If either is not, the client concludes that
the received MessageResponse is erroneous and performs error processing.
– Then the client will attempt to read and process the next SenderLen + MessageLen bytes as the
sender’s name and the message text as described in Section 3.2.
ˆ If, at any point, reading from the socket is not possible without gap, then the client concludes that the
received MessageResponse is erroneous and performs error processing.
3.4 Running your Code
For this assignment, your server and client programs must both run at the same time. To do this:
ˆ Open two terminals on your machine.
ˆ Start the server on one of the terminals, giving the port number to which the server should bind as a
parameter on the command line.
ˆ Then start the client on the other terminal.
– For the server hostname / IP address parameter, you can use the IP address 127.0.0.1 or hostname
localhost, which refers to ‘this host’ without needing to specify the actual IP address of the host.
– For the port parameter, use the port number with which you started the server.
ˆ You should run the client many times (for different users and request types) to thoroughly test your work.
3.4.1 Using Command Line Arguments in Python
Your server and client programs must both take arguments from the command line.
For example, you might write something like the following when running the client on the Linux terminal:
$ python3 client.py 127.0.0.1 5000 "alice stevens" read
This will run the program client.py and pass four command line arguments to it: 127.0.0.1, 5000, alice
stevens, and read.
To access the arguments in Python, you can use the global variable sys.argv, which will contain a Python list of
all the arguments. It is important to note that the first item in this list is always the name of the Python file and
that Python treats all of these as strings (for quoted arguments, the quotes will not be present in the resulting
string). Using the example above:
import sys
...
filename = sys.argv [0] # "client.py"
address = sys.argv [1] # "127.0.0.1"
port = sys.argv [2] # "5000"
name = sys.argv [3] # "alice stevens"
type = sys.argv [4] # "read"
...
3.5 Reading from a Stream/TCP Socket
For a stream (TCP) socket, when a sender sends 50 bytes by writing them in one go into its socket buffer, the
underlying TCP protocol implementation has full discretion over when the data is sent, how many “packets” it
sends, and the size of these packets. Theoretically, the TCP implementation on the sender could choose to send
the first three bytes immediately, then wait three seconds, send the next thirteen bytes, then wait another five
seconds, and then send the remaining 34 bytes. In reality, such large time gaps are unlikely and probably a result
of problems in the underlying network.
In order to detect network issues, you must detect gaps of one second or more and abort operation should this
occur. To achieve this, a socket timeout can be set on each socket when reading or writing data. This can be
done using the socket method settimeout() on the socket. This should be done immediately after creating the
socket (i.e. after a socket() or accept() call). Then, when actually reading from or writing to a socket, e.g.
6
using recv(), you can check if this operation throws an exception. If it does, then you will need to handle any
exceptions (a timeout on the socket will throw a specific exception) by printing an appropriate error message,
returning resources (closing sockets, files, etc. as applicable), and exiting (client) or returning to the start of the
loop (server).
3.6 Data Representation
There are a few things to consider regarding data representation:
ˆ The names and messages that the client reads from the command line or from user input will be treated
by Python as strings. Strings in Python are represented using the UTF-8 character encoding system. In
this system, a printed character may require a variable number of bytes to store in memory. Hence, a string
which looks like it has m characters may in fact need a number n ≥ m of bytes to represent it, and your
client program needs to transmit all n bytes. To achieve this, you can use the encode method in Python to
convert a string to a bytes object. For example:
mystring = "hello world"
mybytes = mystring.encode("utf -8")
and then you can use the len function to find out the length n in bytes of mybytes. You should transmit the
complete bytes object in the required field of the record, and the value of n should be filled into the length
field (’NameLen’, ’MessageLen’, etc.)
ˆ You can use the decode method in Python to convert bytes back into a string. For example:
mystring = mybytes.decode("utf -8")
print(mystring) # "hello world"
ˆ All 16–bit fields in the fixed headers of the MessageRequest and MessageResponse records are encoded in
network byte order. For a 16-bit field, this means that the first or leftmost eight bits are occupied by the
highest-valued bits and the last or rightmost eight bits are occupied by the lowest-valued bits.
4 Deliverables
ˆ Each student must submit their Python source code using the assignment submission activity on the Learn
page of COSC264.
ˆ You must submit two Python (.py) files named server.py and client.py file. If you wish, you may also
submit one additional support file.
ˆ Each Python file must contain the student’s name and student ID in the docstring at the top of the file.
ˆ In order for an assignment to be marked, the student must press the Submit button and then make the
plagiarism declaration to confirm that the assignment is entirely their own work.
ˆ The assignment must be submitted no later than the due date specified on Learn. Students requiring an
extension for special consideration reasons (e.g. illness) must contact the course coordinator.
5 Marking
Marking will be based on the source code. We will consider:
ˆ Its ability to produce the right results (e.g. is packet processing correctly implemented, are all the right socket
calls there, is the gap detection implemented correctly?)
ˆ The amount of error / consistency checking. Appropriate error messages are important to let the user
know exactly what happened and what failed; just letting a library function crash (possibly printing some
nondescript message) will result in deductions. You must also check incoming data / records for correctness.
ˆ Whether you have returned all resources (sockets, files) to the operating system, wherever sensible.
ˆ Style. We may apply deductions if your code is particularly ugly or messy.
essay、essay代写