Skip to content

Commit 5a33898

Browse files
committed
Improved documentation, and fixed issue of needing another file to read messages.
1 parent 310509b commit 5a33898

File tree

4 files changed

+154
-40
lines changed

4 files changed

+154
-40
lines changed

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Simple Socket Chat
2+
3+
Simple chat project, written in Python using sockets for college network class.
4+
5+
## Requirements
6+
7+
In order to run the project, you'll need to have Python 3.7.6 or greater, in case that you already have it you can go to next section: **Running the project**.
8+
If you don't have it you can use [pyenv](https://github.com/pyenv/pyenv) with [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv)
9+
for download python and create your python environment for running the project.
10+
11+
I'm going to leave below a tutorial of how to configure your environment for running the project using `pyenv` with `pyenv-virtualenv`.
12+
13+
### Installing Python with `pyenv`
14+
15+
```bash
16+
$ pyenv install 3.7.6
17+
```
18+
19+
### Creating virtual environment
20+
21+
```bash
22+
$ pyenv virtualenv 3.7.6 socketchat
23+
```
24+
25+
### Using the virtual environment
26+
27+
```bash
28+
$ pyenv activate socketchat
29+
```
30+
31+
## Running the project
32+
33+
For running the project, you will need to have an server to receive and send messages to
34+
clients that are connected, and at least 2 clients to see messages being broadcasted.
35+
36+
- Creating the server:
37+
38+
If you are using Python 3.7.6 without `pyenv`, run the following command below to start
39+
running server:
40+
```bash
41+
$ python3 server.py
42+
```
43+
44+
If you are using `pyenv`, just run the following command to up the server:
45+
```bash
46+
(socketchat) $ python server.py
47+
```
48+
49+
- Creating clients:
50+
51+
Now we are going to need two clients, in order to see each other message on terminal.
52+
53+
If you are using Python 3.7.6 without `pyenv`, run the following command in two different
54+
terminals/bashes in order to create our clients and exchange messagens between them.
55+
```bash
56+
$ python3 client.py
57+
```
58+
59+
Otherwise if you are using `pyenv`, simply run the following code in different terminals/bashes:
60+
```bash
61+
(socketchat) $ python client.py
62+
```
63+
64+
> If you are a client and want to quit from chat, simply write `quit` and you will be
65+
> disconnected from the chat.

client.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,62 @@
1-
import socket
1+
import socket, threading, time
2+
3+
def handle_messages(connection: socket.socket):
4+
'''
5+
Receive messages sent by the server and display them to user
6+
'''
7+
8+
while True:
9+
try:
10+
msg = connection.recv(1024)
11+
12+
# If there is no message, there is a chance that connection has closed
13+
# so the connection will be closed and an error will be displayed.
14+
# If not, it will try to decode message in order to show to user.
15+
if msg:
16+
print(msg.decode())
17+
else:
18+
connection.close()
19+
break
20+
21+
except Exception as e:
22+
print(f'Error handling message from server: {e}')
23+
connection.close()
24+
break
225

326
def client() -> None:
27+
'''
28+
Main process that start client connection to the server
29+
and handle it's input messages
30+
'''
31+
432
SERVER_ADDRESS = '127.0.0.1'
533
SERVER_PORT = 12000
634

735
try:
36+
# Instantiate socket and start connection with server
837
socket_instance = socket.socket()
938
socket_instance.connect((SERVER_ADDRESS, SERVER_PORT))
39+
# Create a thread in order to handle messages sent by server
40+
threading.Thread(target=handle_messages, args=[socket_instance]).start()
1041

42+
print('Connected to chat!')
43+
44+
# Read user's input until it quit from chat and close connection
1145
while True:
12-
msg = input('Insert a message: ')
46+
msg = input()
1347

1448
if msg == 'quit':
1549
break
1650

51+
# Parse message to utf-8
1752
socket_instance.send(msg.encode())
1853

54+
# Close connection with the server
1955
socket_instance.close()
2056

2157
except Exception as e:
2258
print(f'Error connecting to server socket {e}')
59+
socket_instance.close()
2360

2461

2562
if __name__ == "__main__":

messages.py

Lines changed: 0 additions & 27 deletions
This file was deleted.

server.py

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,100 @@
1-
import socket, _thread
1+
import socket, threading
22

3+
# Global variable that mantain client's connections
34
connections = []
45

56
def handle_user_connection(connection: socket.socket, address: str) -> None:
7+
'''
8+
Get user connection in order to keep receiving their messages and
9+
sent to others users/connections.
10+
'''
611
while True:
712
try:
8-
msg = connection.recv(1024).decode()
13+
# Get client message
14+
msg = connection.recv(1024)
915

16+
# If no message is received, there is a chance that connection has ended
17+
# so in this case, we need to close connection and remove it from connections list.
1018
if msg:
11-
print(f'{address[0]}:{address[1]} - {msg}')
12-
13-
msg_to_send = f'From {address[0]}:{address[1]} - {msg}'
14-
19+
# Log message sent by user
20+
print(f'{address[0]}:{address[1]} - {msg.decode()}')
21+
22+
# Build message format and broadcast to users connected on server
23+
msg_to_send = f'From {address[0]}:{address[1]} - {msg.decode()}'
1524
broadcast(msg_to_send, connection)
25+
26+
# Close connection if no message was sent
1627
else:
1728
remove_connection(connection)
1829
break
30+
1931
except Exception as e:
2032
print(f'Error to handle user connection: {e}')
2133
remove_connection(connection)
2234
break
2335

2436

2537
def broadcast(message: str, connection: socket.socket) -> None:
38+
'''
39+
Broadcast message to all users connected to the server
40+
'''
41+
42+
# Iterate on connections in order to send message to all client's connected
2643
for client_conn in connections:
44+
# Check if isn't the connection of who's send
2745
if client_conn != connection:
2846
try:
47+
# Sending message to client connection
2948
client_conn.send(message.encode())
30-
except:
49+
50+
# if it fails, there is a chance of socket has died
51+
except Exception as e:
52+
print('Error broadcasting message: {e}')
3153
remove_connection(client_conn)
3254

55+
3356
def remove_connection(conn: socket.socket) -> None:
57+
'''
58+
Remove specified connection from connections list
59+
'''
60+
61+
# Check if connection exists on connections list
3462
if conn in connections:
63+
# Close socket connection and remove connection from connections list
3564
conn.close()
3665
connections.remove(conn)
3766

3867

3968
def server() -> None:
69+
'''
70+
Main process that receive client's connections and start a new thread
71+
to handle their messages
72+
'''
73+
4074
LISTENING_PORT = 12000
4175

4276
try:
77+
# Create server and specifying that it can only handle 4 connections by time!
4378
socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
44-
4579
socket_instance.bind(('', LISTENING_PORT))
4680
socket_instance.listen(4)
81+
82+
print('Server running!')
4783

4884
while True:
4985

86+
# Accept client connection
5087
socket_connection, address = socket_instance.accept()
51-
88+
# Add client connection to connections list
5289
connections.append(socket_connection)
53-
54-
_thread.start_new_thread(handle_user_connection, (socket_connection, address))
90+
# Start a new thread to handle client connection and receive it's messages
91+
# in order to send to others connections
92+
threading.Thread(target=handle_user_connection, args=[socket_connection, address]).start()
5593

5694
except Exception as e:
5795
print(f'An error has occurred when instancing socket: {e}')
5896
finally:
97+
# In case of any problem we clean all connections and close the server connection
5998
if len(connections) > 0:
6099
for conn in connections:
61100
remove_connection(conn)

0 commit comments

Comments
 (0)