Python代写:CS168-Chat

Chat

In this request, you’ll build a simple application that connects users over a network: a chat server. Similar to chat programs like Slack and IRC, your finished chat server will allow users to converse in different channels. Users can create and join channels; once a user is in a particular channel, all messages that she sends will be relayed to all other users in that channel.
This request (and the rest of the requests in this class) should be implemented in Python 2. This request will introduce you to the socket programming API.

Resources

There’s a demo of the finished project available here.

If you have questions, first take a look at the FAQ section. If your question isn’t answered there, post to Piazza.

The Python Socket Programming HOWTO is a useful introduction to socket programming.

We’ve provided two files to help you test your code:

client_split_messages.py is intended to help you ensure your server is correctly buffering messages, and is described in more detail below.

simple_test.py tests a basic scenario where two clients communicate in a simple channel. simple_test.py represents only a small fraction of the points that we’ll test for when we grade your request, and is intended only to help you verify the basic format of your client’s output.

What are sockets?

A socket is an endpoint of a connection between two programs running across a network. Each socket is associated with a particular port number. Sockets are an abstraction provided by operating systems: programs create sockets, read from those sockets, and write to those sockets. When a program writes to a socket, the operating system sends data out a particular port; similarly, with the operating system receives data on a port, that data can be read from the socket corresponding to that port.

In Python, you can create a socket and connect to a remote endpoint by using the socket library as follows:

1
2
3
4
5
import socket
# The socket constructor accepts a few arguments; the defaults are fine for this class.
client_socket = socket.socket()
client_socket.connect(("1.2.3.4", 5678))
client_socket.sendall("Hello World")

The example above created a socket and connected it to port 5678 at IP address 1.2.3.4. Then, it sent a “Hello World” message to the server at 1.2.3.4:5678.

The example above created a client socket that was connected to exactly one remote endpoint. When you create a server, you’ll typically want to allow multiple remote clients to connect, and you don’t usually know the address of those clients when the socket is created. As a result, server sockets work differently:

1
2
3
server_socket = socket.socket()
server_socket.bind(("1.2.3.4", 5678))
server_socket.listen(5)

After creating the socket, rather than connecting to a particular remote destination, the code above bound the socket to a particular IP address and port, which essentially tells the operating system to associate the given IP address and port with the socket. Finally, the listen call listens for connections made to the socket. When a new client connects to the socket, the socket library will create a new socket to use to communicate with that client, so that the server socket can continue to be used to wait for inbound connections from other clients:

1
(new_socket, address) = server_socket.accept()

This call blocks until a client connects (using a connect() call, as in the example above), and then returns a newly created socket, new_socket, that can be used to send and receive data to and from the client. For example, the call

1
message = new_socket.recv(1024)

will block until there is data to receive from the client, and will return up to 1024 bytes of data.
You’ll need to do some reading to understand how all of these API calls work. In particular, be careful when using send and recv! send, for example, will not necessarily send all of the data passed into it. The Python Socket Programming HOWTO will likely be a useful resource.

Part 0 (to be completed in section)

The first part of the request will help you get started with the socket programming API and introduce you to basic client-server interaction. This part of the request will not be graded, and you’ll complete it.

For this part of the request, you’ll write a simple client and server. The client will send a single message from stdin to the server and then disconnect. The server should print messages it receives to stdout. If multiple clients connect to the server, the server should handle them sequentially (i.e., it should print the complete message from one client and close that connection before handling the next client).

The server should accept one command line argument, stating the port that the server should use:

1
python basic_server.py 12345

The client should accept two command line arguments: the hostname (or IP address) of the server to connect to, and the server port:

1
$ python basic_client.py localhost 12345

Your server should be reachable from any IP address associated with the machine (see the FAQ).

Blocking sockets

In this part of the request, it’s fine to use blocking sockets. “Blocking” means that a socket call may not return for a while, until the call completes. Cases when socket calls won’t complete immediately include:

  • send: if the socket’s internal buffer is full so no data can be written
  • recv: if the internal buffer is empty, so there’s no data to read (e.g., if the client has paused sending)
  • accept: if these are no clients currently trying to connect

Example use

Here’s an example of how your client and server should work. Suppose two different clients connected sequentially:

1
2
3
4
$ python basic_client.py localhost 12345
I am a student in CS168. This class is awesome!
$ python basic_client.py localhost 12345
Why is Shenker so bad at drawing?

If a server had been started on port 12345 before the client was run, it should have printed output as follows:

1
2
3
$ python basic_server.py 12345
I am a student in CS168. This class is awesome!
Why is Shenker so bad at drawing?

Part 1

In the remainder of the request, you’ll build on your basic client and server to create a chat server with different channels that clients can communicate on. For a demo of how your server should behave, watch the video here.

Details

We’ve provided a utils.py file that has error messages that you should use. These are intended to make your life easier, and also to enable testing. Be sure you use these messages; otherwise, your code will fail the tests!

Non-blocking sockets
You’ll need to use non-blocking sockets for this part of the request, because both your client and server need to receive data from multiple sources, in an unknown order. Consider what would happen if your client used blocking sockets, as in part 0, with a call like:

1
message_from_server = client_socket.recv(200)

Now suppose that the server doesn’t send any messages for a while, but while the client is blocked waiting on the recv call to return, the user types some data into stdin. The client should read the data from stdin and send it to the server – but the client is stuck blocked waiting on data from the server socket! To address this problem, you can use non-blocking sockets.

To use non-blocking sockets, you’ll need to use the select call in the select library. For more about how to use select and a very relevant example, take a look at this page. While you are required to use non-blocking sockets for reading data and accepting connections, it’s fine to use blocking sockets for sending messages (since the messages you’re sending are short and you don’t need to handle sending a large number of messages in quick succession, send and sendall should not block for long periods of time).