Home CPSC 414

Chat Client

Due: February 26

Objective

To gain experience writing a socket program interacting with a server using a protocol.


Task

For this project, you will write a chat client which will connect to a chat server. Your client will allow users to connect to the chat server, create or join chat rooms and chat with other users.

You will connect to a server running at 34.73.23.1 on port 5220. This is a very simple chat server developed for this project. Your chat client should connect to this server and, following the protocol described below, let the user chat with any others logged in.

Note that this server will be used by others in this class, and they can see what you are sending (if theirs is working anyway)!


The Protocol

Right after you connect to the server, you will be in a "lobby" of sorts. At that point you can do one of two things:

  1. Ask for a list of rooms. To do this, send "/list" to the server. It will then send you back an integer giving the number of existing chat rooms. Next it will send you the names of them all.
  2. Join a room. To do this, send "/join ROOM" where ROOM is the name of the chat room you wish to join. This can be an existing room, or a new one. The server will send you a "0" to indicate that this was successful, or a "1" to indicate that your request was not formatted correctly.

After the user has joined a room, they must choose a nickname. This can be done by sending "/nick NAME" to the server where NAME is the nickname the user has chosen. The server will send "0" to indicate success, "1" to indicate the request was formatted incorrectly, or "2" to indicate that the name is taken (all nick names in a room have to be unique).

Once they have picked a name, they will be in the chat room. At this point, the server will send the client messages whenever anyone talks. Likewise, the client can send the server a message at any time. These messages will just be plain strings.

From within the room, there are two "special" messages the client can send to the server:

  1. To ask for a list of users in the room, one can send "/who". Afterwards, the server will send you the names of the users, one after the other.
  2. To leave the chat room, you can send "/logout". The server will then remove you from the system (you don't go back to the lobby, you just disconnect).

You can assume all messages from the server will be 1024 bytes or fewer. The server makes the same assumptions about what you send.


Requirements

  1. You client needs to establish a connection to the server.
  2. Next you should ask the user of your program if they want to list the rooms or join one, and then deal with the server appropriately. If it's not formatted correctly, you must continue to ask them what room to join.
  3. Next, ask for the user name, and send it to the server. If it is taken, or not formatted correctly, you must continue to ask them for a name.
  4. Once in the room, you will read from the server, and print any messages received.
  5. You must also read from the user of the program and send those messages to the server.
  6. If the user types the "/logout" command, you should close the connection and quit the program after sending it.

You have flexibility in how the interface to your program should work, but you need to make it clear to the user how to do all of these things. You also should not just dump the server messages (like "1" or "2" for errors), but should give a sensible message.


Using select

One issue you will run into with writing the client, is that we must read from both the server, with our socket, and also the user's input. The issue is that the socket recv method, and the Python input function are blocking.

This means code like this won't work well:


# read from server
mesg = sock.recv(1024)

# read from user
in = input()

When recv is called, it will block, meaning it won't return until the socket actually sends something. If the socket sends data before the user, that's OK. But if not, our code won't get the user's message until the server sends. Flipping them in the other order makes a similar issue.

Instead we need to wait for either one to have data available and handle whichever one is ready first. This is done with select. We must first import the select module.

The function works by taking three lists. The first is the list of things we want to read from. The second is a list of things we want to write to. The third is a list of things we might expect errors on. It returns three lists as well, which are those things which are ready. If we only care about reading from a socket and the user, we can call it like this:


# for the select function
import select

# for the stdin file
import sys

# wait for either our socket or user
reads, writes, errors = select.select([sock, sys.stdin], [], [])

The select function will block until one of the things is ready. In this case, when sock and/or sys.stdin are ready to be read. The reads variable will be a list of things waiting to be read. If it contains sock, then we can recv from the socket. If it contains sys.stdin, we can get input from the user right away.


General Requirements

When writing your program, also be sure to:


Submitting

To submit your program, email the program file to ifinlay@umw.edu.

Copyright © 2018 Ian Finlayson | Licensed under a Creative Commons Attribution 4.0 International License.