CSSE2310/CSSE7231 Computer Systems Principles and Programming Assignment 4: Real-time, online auction platform
CSSE2310/CSSE7231 — Semester 2, 2023 Assignment 4 (version 1.0)
Introduction 1 The goal of this assignment is to further develop your C programming skills, and to demonstrate your un- 2
derstanding of networking and multithreaded programming. You are to create two programs which together 3 implement a real-time, online auction platform. The main program – auctioneer – is a network server which 4 accepts connections from clients (including auctionclient which you will implement). Clients connect, and 5 interact with auctioneer in various ways to sell and buy items.2263 6
Communication between the clients and auctioneer is over TCP using a newline-terminated text command 7 protocol. Advanced functionality such as connection limiting, signal handling and statistics reporting are also 8 required for full marks. CSSE7231 students shall also implement a simple HTTP interface to auctioneer. 9
The assignment will also test your ability to code to a particular programming style guide and to use a 10 revision control system appropriately. 11
Student Conduct 12 13
This is an individual assignment. You should feel free to discuss general aspects of C programming and 14 the assignment specification with fellow students, including on the discussion forum. In general, questions like 15 “How should the program behave if ⟨this happens⟩?” would be safe, if they are seeking clarification on the 16 specification. 17
You must not actively help (or seek help from) other students or other people with the actual design, structure 18 and/or coding of your assignment solution. It is cheating to look at another student’s assignment code 19 and it is cheating to allow your code to be seen or shared in printed or electronic form by others. 20 All submitted code will be subject to automated checks for plagiarism and collusion. If we detect plagiarism or 21 collusion, formal misconduct actions will be initiated against you, and those you cheated with. That’s right, if 22 you share your code with a friend, even inadvertently, then both of you are in trouble. Do not post your 23 code to a public place such as the course discussion forum or a public code repository. (Code in private posts 24 to the discussion forum is permitted.) You must assume that some students in the course may have very long 25 extensions so do not post your code to any public repository until at least three months after the result release 26 date for the course (or check with the course coordinator if you wish to post it sooner). Do not allow others to 27 access your computer – you must keep your code secure. Never leave your work unattended. 28
Code Origin
Code examples found in man pages on moss.
Online auctions 53
In a simple auction, an item is put up for sale by a seller. One or more buyers then place bids – statements 54 describing how much they are willing to pay for the item. Each bid must be higher than the previous. Buyers 55 may place multiple bids, so long as each is higher than the current maximum bid. Once a buyer has set the 56 highest bid, they may not bid again until a different buyer places a higher bid on the item. At the end of the 57 auction, the highest bidder is the winner. 58
The seller sets a reserve price – the lowest amount they are willing to sell the item for. If the auction does 59 not reach the reserve price, the auction ends with the item unsold. The item is described as being passed in, 60 and there is no winner. Any bids below the reserve price are rejected.2263 61
The auctioneer manages the auction. They receive items and their reserve prices from sellers, and accept 62 bids from potential buyers. The auctioneer is responsible for rejecting invalid bids – such as ones that do not 63 exceed the current maximum bid, and those that are below the reserve. 64
Online auctions face some challenges – automated buying agents can attempt to wait until the very last 65 moments of an auction, and place a bid that is fractionally above the current highest bid, seeking to win the 66 auction for the lowest possible price. This strategy is known as sniping. Because sniping can give an unfair 67 advantage over regular, human-placed bids, it is common to place limits on how frequently bids may be made. 68
In this assignment, all auctions are time-limited, with the duration being specified by the seller when placing 69 the item up for auction. The auctioneer is the final arbiter of time. Bids received after an auction has finished, 70 even if they were sent before expiration, are rejected.2263 71
Specification – auctionclient 72
The auctionclient program provides a command line interface that allows you to interact with the server 73 (auctioneer) as a client – connecting, placing items for sale, finding items available, bidding on other auctions 74 currently underway, and receiving and displaying the results of the auctions on your items. 75
To implement this functionality, auctionclient will probably require two threads – one for handling stdin 76 and sending commands to the server, and another for handling messages from the server. You may optionally 77 use a single thread and multiplexed I/O, such as poll() or select(), however this is not required. 78
If the correct number of arguments is provided, then further errors are checked for in the order below. 87 Connection error 88
If auctionclient is unable to connect to the auction server on the specified port (or service name) of localhost, 89 it shall emit the following message (terminated by a newline) to stderr and exit with status 20: 90
auctionclient: connection failed to port N
where N should be replaced by the argument given on the command line. (This may be a non-numerical string.) 91
auctionclient runtime behaviour 92 Assuming that the commandline arguments are without errors, auctionclient is to perform the following 93
actions: 94 • connect to the server on the specified port number (or service name) – see above for how to handle a 95
connection error;
read commandsIf the network connection to the server is closed (e.g. auctionclient detects EOF on the socket), then auctionclient shall emit the following message to stderr and terminate with exit status 5:
auctionclient: server has gone away
auctionclient shall interpret its input from stdin as follows: 99
Blank lines (i.e. those lines containing no characters), and those beginning with the # character (comment 100
lines), shall be ignored. (“beginning with” means the # character is in the leftmost position on the line.) 101
The command quit on a line by itself shall cause the auctionclient to exit (with exit status 0) if the 102 client is not currently involved in any auctions, i.e., any auctions for which that client submitted an 103 item for sale are completed, and that client is not currently the highest bidder on any auctions. If those 104 conditions are not true, then the client must print (and flush) the following message to stdout: 105
Auction in progress - can not exit yet
and must not exit. 106
All other lines shall be sent unaltered to the server (no error checking is required or to be performed) 107
If auctionclient detects EOF on stdin then it will exit. If the client still has auctions in progress (i.e. it 108 has offered one or more items for sale and those auctions haven’t expired, or if it is currently the highest bidder 109 on one or more auctions) then auctionclient must print the following to stderr and exit with exit status 4: 110
Quitting with auction(s) still in progress
Otherwise (i.e. the client is not involved in any current auctions), it will print nothing and exit with exit 111 status 0. 112 Upon sending a command to the server, auctionclient shall expect a single line reply as per the commu- 113 nication protocol described in Communication protocol. 114 Other than SIGPIPE ( which is required to gracefully handle the unexpected disconnection by the server) 115 your auctionclient program is not to register any signal handlers nor attempt to mask or block any signals. 116
auctionclient termination 117
The recommended architecture for the auctionclient is to have one thread handling stdin and sending 118 commands to the server, and another thread for receiving and interpreting responses from the server. Since 119 blocking reads will be performed on stdin and the server socket, it is difficult to use the methods described in 120 lectures for cleaning up threads (e.g. a “terminate“ flag variable in a shared data structure). For this reason, 121 when either thread detects a valid termination condition for the program, it may simply call 122 exit() immediately – you should not attempt to use pthread_cancel() or any other, more complicated 123 termination mechanism.2263 124
The command protocol (see Communication protocol) is designed such that auctionclient can easily keep 125 track of how many auctions it is currently involved in – both as a seller and/or a buyer. All commands sent 126 to auctioneer, whether correct or not, generate replies. By interpreting these replies, auctionclient can 127 determine when all of its auctions have completed. For the purposes of determining this condition, the client 128 may assume that the server is correct and well-behaved. 129
$ ./auctionclient 49152 |
# A comment line - ignored
# A blank line - ignored
# An invalid item listing, missing an argument sell chicken 10 |
1 2 3 4 5 6 7
Version 1.0
# A simple item listing, reserve 10, 30 second auction sell chicken 10 30 sell chicken 20 60
:rejected |
# Attempt to bid on own item bid chicken 1000 sell potato 10 30
:listed potato
|
# Invalid listing - non-numeric reserve price # Fast auction on a cow (5 seconds)
|
sell cow 1000 5
:listed cow # See all items available (note our items are listed but so too is pumpkin |
# Bid on the pumpkin, which is accepted (...time passes, fast cow auction finishes ...)
|
:sold cow 2500
# try to quit, but potato still unsold |
# now the quit command can succeed
quit
|
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Note that not all possible error conditions are present in this example. 135
Further note that the auctionclient must keep track of items it has listed as they are sold or 136 passed-in – this is the only way it can determine whether it can exit or not. Similarly, it must 137 also keep track of items for which it is the highest bidder. 138
Specification – auctioneer 139
auctioneer is a networked, multithreaded auction server allowing clients to connect, submit items for sale, bid 140 on items, and list the items currently available. All communication between clients and the server is over TCP 141 using a simple command protocol that is described in the Communication protocol section.2263 142
The num-connections argument, if specified, indicates the maximum number of simultaneous client con- 148 nections to be permitted. If this is zero or absent, then there is no limit to how many clients may connect (other 149 than operating system limits which we will not test). 150
The portnum argument, if specified, indicates which localhost port auctioneer is to listen on. If the port 151 number is absent or zero, then auctioneer is to use an ephemeral port.2263 152 Important: Even if you do not implement the connection limiting functionality, your program must correctly 153 handle command lines which include either of those arguments (after which it can ignore any provided values 154 – you will simply not receive any marks for that feature). 155
Program Operation 156 The auctioneer program is to operate as follows: 157
If the program receives an invalid command line then it must print the message: 158
Usage: auctioneer [--maxconn num-connections] [--port portnum]
to stderr, and exit with an exit status of 19. 159 Invalid command lines include (but may not be limited to) any of the following: 160
– either of --maxconn or --port does not have an associated value argument 161
– the num-connections argument (if present) is not a non-negative integer 162
– the port number (portnum ) argument (if present) is not an integer value, or is an integer value and 163 is not either zero, or in the range of 1024 to 65535 inclusive 164
– any of the arguments is specified more than once 165
– any additional arguments are supplied 166
If portnum is missing or zero, then auctioneer shall attempt to open an ephemeral localhost port for 167 listening. Otherwise, it shall attempt to open the specified port number. If auctioneer is unable to listen 168 on either the ephemeral or specified port, it shall emit the following message to stderr and terminate 169 with exit status 4: 170
auctioneer: unable to open socket for listening
Once the port is opened for listening, auctioneer shall print to stderr the port number followed by a 171 single newline character and then flush the output. In the case of ephemeral ports, the actual port 172 number obtained shall be printed, not zero.2263 173
Upon receiving an incoming client connection on the port, auctioneer shall spawn a new thread to handle 174 that client (see below for client thread handling). 175
If specified (and implemented), auctioneer must keep track of how many active client connections exist, 176 and must not let that number exceed the connections parameter. See below on client handling threads 177 for more details on how this limit is to be implemented. 178
Note that all error messages above must be terminated by a single newline character. 179
The auctioneer program should not terminate under normal circumstances, nor should it block or oth- 180
erwise attempt to handle SIGINT. 181
Note that your auctioneer must be able to deal with any clients using the correct communication protocol, 182
not just the client programs specified for the assignment. Testing with netcat is highly recommended. 183 Client handling threads 184
A client handler thread is spawned for each incoming connection. This client thread must then wait for commands 185 from the client, one per line, over the socket. The exact format of the requests is described in the Communication 186 protocol section below. 187
Auction running algorithm 193 The auction is run according to the following algorithm. Refer to the Communication protocol section for details 194
of message syntax and required responses. 195
auctioneer receives new items to auction via the sell command. The arguments to this command are 196 validated and if correct, the item is now available for sale. If the command is invalid, the client is informed 197 and the sell command does not succeed.2263 198
auctioneer receives bids on sale items via the bid command. If the command syntax is valid, the item 199 exists and is currently available, and the bid on the item is higher than any previous bid on the same 200 item, then the bidding client is remembered as the current winning bidder for that item. If there was a 201 previous winning bidder, that client is sent an outbid message informing it that it has been outbid on the 202 item. If an invalid bid command is received, then the bidding client is informed and the bid is otherwise 203 ignored by auctioneer. 204
auctioneer accepts the list command from any connected client, regardless of whether that client has 205 listed or bid on any items. The list command returns a formatted list describing each item available, its 206 reserve price, current maximum bid, and the time remaining in the auction for that item. 207
When the auction on any item completes (reaches the expiry time), auctioneer takes one of the two 208 following actions: 209
– If there is a winning bid which is greater than or equal to the reserve price, then the winning bidder is 210 sent a win message identifying the item and the winning bid amount. The seller of the item receives 211 a sold message indicating the item that was sold and the winning bid amount. 212
– If there were no bids on the item, or no bids at or above the reserve price, then auctioneer sends 213 a unsold message to the seller, informing it that the item did not sell. No message is sent to any 214 previous bidding client in the event that the item goes unsold. 215
Advanced: auctioneer is to limit bids from clients to a maximum of one bid per item per second. That 216 is, if a client has an accepted bid on an item more recently than one second ago, a rejected message is 217 sent in reply. 218
About time 219
auctioneer relies on time to determine when auctions expire. Expiry time is at the resolution of milliseconds. 220 The following approach to handling time and auction expiry is recommended. You may choose another approach 221 if you wish as long as the behaviour is the same. 222
the provided library function “double get_time_ms(void)” returns a floating point time value in seconds, 223 to millisecond resolution, e.g. the value 16.745 represents 16.745 seconds. 224
for each auction item, store an expiry time (not a duration). So, as each new item is listed, store its expiry 225 time calculated by (get_time_ms() + duration). 226
to report auction time remaining (e.g. for the list command), calculate (item.expiry - get_time_- 227 ms())) which will yield a floating point value, in seconds, until the auction expires. Note that the list 228 command shall report time remaining rounded down to the nearest second – see the Communication 229 protocol section for details. 230
The simplest way to check on expiring auctions is to run a dedicated server thread which periodically checks 231 the current time (get_time_ms()) against each item’s expiry time, and marks as ‘complete’ any such items 232 which have expired, and handle any necessary communication and cleanup associated with expired auctions. 233 Be mindful of locking and mutual exclusion, because client threads may also be accessing the auction data 234 structures while this is taking place. 235
Disconnecting clients 240
If a client disconnects before the end of an auction, (e.g. a read() or equivalent) from the client returns EOF, 241 or a write() or equivalent fails), the auctioneer is to simply ignore that fact for the purposes of the auction, 242 and close the network connection to that client, cleaning up data structures as required. 243
If a client disconnects, any auctions it was involved with proceeds until they naturally expires. 244 So, if a disconnected client was selling an item, other clients may still bid on the item in the normal manner. 245 When the auction expires, auctioneer will inform the winning bidder, but will not attempt to send the sold 246 message to the now-disconnected seller. 247
Similarly if a bidder disconnects mid-auction, the auction proceeds and their bid remains valid. 248 If this disconnected client ends up winning the auction, the selling client still receives the sold message, there 249 is just no attempt to communicate to the now-disconnected bidder. If a disconnected client gets outbid, the 250 outbid message is not sent. 251
SIGHUP handling (Advanced) 252 Upon receiving SIGHUP, auctioneer is to emit (and flush) to stderr statistics reflecting the program’s operation 253
to-date, specifically 254 • Total number of clients connected (at this instant) 255 • The total number of clients that have connected and disconnected since program start 256 • The number of active auctions in progress (at this instant) 257 • The total number of sell requests received (since program start), including invalid2. and rejected requests 258 • The total number of accepted sell requests received (since program start) 259 • The total number of bid requests received (since program start), including invalid3 and rejected bids 260 • The total number of accepted bid requests received (since program start) 261
The required format is illustrated below. Each of the seven lines is terminated by a single newline. You can 262 assume that all numbers will fit in a 32-bit unsigned integer, i.e. you do not have to consider numbers larger 263 than 4,294,967,295.2263 264
Listing 1: auctioneer SIGHUP stderr output sample
Connected clients: 4
|
Completed clients: 20
Active auctions: 2
Total sell requests: 20
Successful sell requests: 14
Total bid requests: 34
Successful bid requests: 31
|
1 2 3 4 5 6 7
Note that to accurately gather these statistics and avoid race conditions, you will need some sort of mutual 265 exclusion protecting the variables holding these statistics. 266 Global variables are NOT to be used to implement signal handling. See the Hints section below for how you 267 can implement this. 268
Client connection limiting (Advanced) 269
If the --maxconn num-connections feature is implemented and a non-zero command line argument is provided, 270 then auctioneer must not permit more than that number of simultaneous client connections to the server. 271 auctioneer shall maintain a connected client count, and if a client beyond that limit attempts to connect, it 272 shall block, indefinitely if required, until another client leaves and this new client’s connection request can be 273 accept()ed. Clients in this waiting state are not to be counted in statistics reporting – they are only counted 274 once they have properly connected.2263 275
HTTP connection handling (CSSE7231 students only) 276
CSSE7231 students shall, in addition, implement a simple HTTP server in their auctioneer implementa- 277 tion. Upon startup, auctioneer shall check the value of the environment variable A4_HTTP_PORT. If set, then 278 auctioneer shall also listen for connections on that port number (or service name). 279
If auctioneer is unable to listen on that port or service name then it shall emit the following message to 280 stderr and terminate with exit status 8: 281
auctioneer: unable to open HTTP socket for listening
The ability to listen on this port is checked after the ability to listen on the “main” port (i.e. the one given 282 on the command line). If the A4_HTTP_PORT environment variable is not set then auctioneer shall not listen 283 on any additional ports and shall not handle these HTTP connections. 284
The communication protocol uses HTTP. The connecting program (e.g. netcat, or a web browser) shall 285 send HTTP requests and auctioneer shall send HTTP responses as described below. The connection between 286 client and server is kept alive between requests. Multiple HTTP clients may be connected simultaneously. 287
Additional HTTP header lines beyond those specified may be present in requests or responses and must be 288 ignored by the respective server/client. Note that interaction between a client on the HTTP port and auctioneer 289 is synchronous – any one client can only have a single request underway at any one time. This greatly simplifies 290 the implementation of the auctioneer HTTP connection handling threads.2263 291
The only supported request method is a GET request in the following format: 292
• Request: GET /stats HTTP/1.1 293
– Description: the client is requesting statistics from auctioneer 294 – Request 295 ∗ Request Headers: none expected, any headers present will be ignored by auctioneer. 296 ∗ Request Body: none, any request body will be ignored 297 – Response 298
∗ Response Status: 200 (OK). 299
∗ Response Headers: the Content-Length header with correct value is required (number of bytes 300 in the response body), other headers are optional. 301
∗ Response Body: the ASCII text containing the same auctioneer statistics information described 302 in the SIGHUP handling section above. 303
If auctioneer receives an invalid HTTP request then it should close the connection to that requestor (and 304 terminate the thread associated with that connection). Similarly, if a HTTP client disconnects, auctioneer 305 should handle this gracefully and terminate the thread associated with the connection. HTTP clients are NOT 306 included in the client count statistics. 307
Program output 308 Other than error messages, the listening port number, and SIGHUP-initiated statistics output, auctioneer is 309
not to emit any output to stdout or stderr. 310 Communication protocol 311
The communication protocol between clients and auctioneer uses simple newline-terminated text message 312 strings as described below. Note that the angle brackets <foo> are used to represent placeholders, and are not 313 part of the command syntax.2263 314
Supported messages from a client to auctioneer are: 315
sell <item> <reserve> <duration> – the client is requesting to sell the item called <item>, with a 316 reserve price of <reserve>, with the auction lasting for <duration> seconds. 317 See below for the conditions that must be met for a sell command to be accepted. 318
bid <item> <amount> – the client is requesting to bid <amount> on the specified <item>. 319 See below for the conditions that must be met for a bid command to be accepted. 320
Single spaces are used as separators between fields, which implies that <item> must not contain spaces. 322 All numerical values must be valid decimal integers (and subject to additional value requirements as described 323 below). 324
Supported messages from auctioneer to a client are as follows. Error conditions in messages should be 325 tested in this order as well – first look for “invalid” commands, then check to see if the command should be 326 “rejected”: 327
:invalid – sent by the server to a client if any of the following conditions are true: 328
– Invalid command word – an empty string or a command word which is not sell, bid or list 329 – Too few or too many arguments 330 – An invalid value for a numerical field, including 331
∗ not a properly formed integer (e.g. “10.2”, “12p” etc) 332 ∗ a negative reserve price in a sell command 333 ∗ a duration of less than 1 second in a sell command 334 ∗ a bid value of less than 1 in a bid command 335
– Any other kind of malformed message 2263 336
:rejected – sent by the server to a client if one any of the following conditions is true: 337
– a client attempts to bid on an item which is not for sale 338
– a client attempts to bid on an item for which they are already the highest bidder 339
– the client sends a bid command but the bid amount is less than the reserve price or less than or 340
equal to the current maximum bid
– a client attempts to bid on an item that they themselves have listed for sale
– the client sent a sell command but there is already an item for sale by the same name
:listed <item> – sent to the client when <item> is successfully listed for auction
:bid <item> - sent to the client when a bid is successfully placed on <item>
response to a list command, in the following format:
341 342 343
344
345
346
347 <remainN>| 348 349 That is, for each item currently available for auction, the name, reserve price, maximum bid so far, 350 and remaining time (rounded down to the nearest second) is emitted in a space-separated format, fol- 351 lowed by a vertical bar “|” character. Items should be reported in the same order that they were 352 submitted to auctioneer – there is no need to perform additional sorting on the list, simply 353 storing them in order of arrival is sufficient. 354 355 If there are no items currently available for auction, then the string “:list” is returned. 356
<maxBidN>
Provided Libraries 364 libcsse2310a4 365
A split_by_char() function is available to break a line up into multiple parts, e.g. based on spaces. This is 366 similar to the split_line() function from libcsse2310a3 though allows a maximum number of fields to be 367 specified. 368
The get_time_ms() function will return a millisecond-precision, monotonically increasing time value to 369 support all time-related functions in this assignment. The specific value returned is not particularly meaningful, 370 however the difference between values returned from calls to this function can be used to accurately measure 371 the passage of time. 372
Several additional library functions have been provided to aid CSSE7231 students in parsing/construction 373 of HTTP requests/responses. The functions in this library are: 374
double get_time_ms(void); |
char** split_by_char(char* str, char split, unsigned int maxFields); int get_HTTP_request(FILE *f, char **method, char **address, HttpHeader ***headers, char **body); |
HttpHeader** headers, char* body); HttpHeader*** headers, char** body); void free_header(HttpHeader* header); |
void free_array_of_headers(HttpHeader** headers); |
These functions and the HttpHeader type are declared in /local/courses/csse2310/include/csse2310a4.h 375 on moss and their behaviour is described in man pages on moss – see get_time_ms(3), split_by_char(3), 376 get_HTTP_request(3), and free_header(3). 377lectures. You can also use the provided demo programs demo-auctionclient and demo-auctioneer. 394
The read_line() function from libcsse2310a3 may be useful in both auctionclient and auctioneer. 395
The multithreaded network server example from the lectures can form the basis of auctioneer. 396
Use the provided library functions (see above).2263 397
Consider a dedicated signal handling thread for SIGHUP. pthread_sigmask() can be used to mask signal 398 delivery to threads, and sigwait() can be used in a thread to block until a signal is received. You will 399 need to do some research and experimentation to get this working. Be sure to properly reference any code 400 samples or inspiration you use. 401
Possible Approach 402
Try implementing auctionclient first. (The programs are independent so this is not a requirement, but 403 when you test it with demo-auctioneer it may give you a better understanding of how auctioneer 404 works.) 405
For auctioneer, start with the multithreaded network server example from the lectures, gradually adding 406 functionality for supported message types.2263 407
Design a simple data structure for auctioneer to store items for sale. This data structure will be accessed 408 by client threads to add new items, determine validity of bids, and respond to list commands. It will 409 also need to be accessed by whichever thread checks for auction expiry. Locking and mutual exclusion will 410 be essential, but don’t over-complicate it! 411