Skip to content

Commit 9b8edcf

Browse files
author
Melle Dijkstra
committed
Updated README and added comments
1 parent cb29eb6 commit 9b8edcf

5 files changed

Lines changed: 148 additions & 7 deletions

File tree

README.md

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,117 @@
1-
# python-grpc-chat
1+
# Python gRPC Chat
22
Chat application created with gRPC. This was a study for bidirectional gRPC streaming.
3+
4+
## Server
5+
6+
```python
7+
class ChatServer(rpc.ChatServerServicer):
8+
9+
def __init__(self):
10+
# List with all the chat history
11+
self.chats = []
12+
13+
# The stream which will be used to send new messages to clients
14+
def ChatStream(self, request_iterator, context):
15+
"""
16+
This is a response-stream type call. This means the server can keep sending messages
17+
Every client opens this connection and waits for server to send new messages
18+
19+
:param request_iterator:
20+
:param context:
21+
:return:
22+
"""
23+
lastindex = 0
24+
# For every client a infinite loop starts (in gRPC's own managed thread)
25+
while True:
26+
# Check if there are any new messages
27+
while len(self.chats) > lastindex:
28+
n = self.chats[lastindex]
29+
lastindex += 1
30+
yield n
31+
32+
def SendNote(self, request: chat.Note, context):
33+
"""
34+
This method is called when a clients sends a Note to the server.
35+
36+
:param request:
37+
:param context:
38+
:return:
39+
"""
40+
print("[{}] {}".format(request.name, request.message))
41+
# Add it to the chat history
42+
self.chats.append(request)
43+
return chat.Empty()
44+
```
45+
46+
## Client
47+
48+
```python
49+
address = 'localhost'
50+
port = 11912
51+
52+
53+
class Client:
54+
55+
def __init__(self, u: str, window):
56+
# the frame to put ui components on
57+
self.window = window
58+
self.username = u
59+
# create a gRPC channel + stub
60+
channel = grpc.insecure_channel(address + ':' + str(port))
61+
self.conn = rpc.ChatServerStub(channel)
62+
# create new listening thread for when new message streams come in
63+
threading.Thread(target=self.__listen_for_messages, daemon=True).start()
64+
self.__setup_ui()
65+
self.window.mainloop()
66+
67+
def __listen_for_messages(self):
68+
"""
69+
This method will be ran in a separate thread as the main/ui thread, because the for-in call is blocking
70+
when waiting for new messages
71+
"""
72+
for note in self.conn.ChatStream(chat.Empty()):
73+
print("R[{}] {}".format(note.name, note.message))
74+
self.chat_list.insert(END, "[{}] {}\n".format(note.name, note.message))
75+
76+
def send_message(self, event):
77+
"""
78+
This method is called when user enters something into the textbox
79+
"""
80+
message = self.entry_message.get()
81+
if message is not '':
82+
n = chat.Note()
83+
n.name = self.username
84+
n.message = message
85+
print("S[{}] {}".format(n.name, n.message))
86+
self.conn.SendNote(n)
87+
88+
...
89+
```
90+
91+
## The proto file
92+
93+
```proto
94+
syntax = "proto3";
95+
96+
package grpc;
97+
98+
message Empty {}
99+
100+
// I called it Note because message Message is annoying to work with
101+
message Note {
102+
string name = 1;
103+
string message = 2;
104+
}
105+
106+
service ChatServer {
107+
// This bi-directional stream makes it possible to send and receive Notes between 2 persons
108+
rpc ChatStream (Empty) returns (stream Note);
109+
rpc SendNote (Note) returns (Empty);
110+
}
111+
```
112+
113+
# DEMO
114+
115+
![Chat System](chat-system-inaction.gif)
116+
117+
![Chat System](chat-system.png)

chat-system-inaction.gif

231 KB
Loading

chat-system.png

165 KB
Loading

client.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,30 @@
1414
class Client:
1515

1616
def __init__(self, u: str, window):
17+
# the frame to put ui components on
1718
self.window = window
1819
self.username = u
20+
# create a gRPC channel + stub
1921
channel = grpc.insecure_channel(address + ':' + str(port))
2022
self.conn = rpc.ChatServerStub(channel)
21-
#
23+
# create new listening thread for when new message streams come in
2224
threading.Thread(target=self.__listen_for_messages, daemon=True).start()
2325
self.__setup_ui()
2426
self.window.mainloop()
2527

2628
def __listen_for_messages(self):
29+
"""
30+
This method will be ran in a separate thread as the main/ui thread, because the for-in call is blocking
31+
when waiting for new messages
32+
"""
2733
for note in self.conn.ChatStream(chat.Empty()):
2834
print("R[{}] {}".format(note.name, note.message))
2935
self.chat_list.insert(END, "[{}] {}\n".format(note.name, note.message))
3036

3137
def send_message(self, event):
38+
"""
39+
This method is called when user enters something into the textbox
40+
"""
3241
message = self.entry_message.get()
3342
if message is not '':
3443
n = chat.Note()

server.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,38 @@
1010
class ChatServer(rpc.ChatServerServicer):
1111

1212
def __init__(self):
13+
# List with all the chat history
1314
self.chats = []
14-
pass
1515

16+
# The stream which will be used to send new messages to clients
1617
def ChatStream(self, request_iterator, context):
18+
"""
19+
This is a response-stream type call. This means the server can keep sending messages
20+
Every client opens this connection and waits for server to send new messages
21+
22+
:param request_iterator:
23+
:param context:
24+
:return:
25+
"""
1726
lastindex = 0
27+
# For every client a infinite loop starts (in gRPC's own managed thread)
1828
while True:
29+
# Check if there are any new messages
1930
while len(self.chats) > lastindex:
20-
n = chat.Note()
21-
n.name = self.chats[lastindex].name
22-
n.message = self.chats[lastindex].message
31+
n = self.chats[lastindex]
2332
lastindex += 1
2433
yield n
2534

2635
def SendNote(self, request: chat.Note, context):
36+
"""
37+
This method is called when a clients sends a Note to the server.
38+
39+
:param request:
40+
:param context:
41+
:return:
42+
"""
2743
print("[{}] {}".format(request.name, request.message))
44+
# Add it to the chat history
2845
self.chats.append(request)
2946
return chat.Empty()
3047

@@ -33,11 +50,11 @@ def SendNote(self, request: chat.Note, context):
3350
port = 11912
3451
# create a gRPC server
3552
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
36-
3753
rpc.add_ChatServerServicer_to_server(ChatServer(), server)
3854

3955
print('Starting server. Listening...')
4056
server.add_insecure_port('[::]:' + str(port))
4157
server.start()
58+
# Server starts in background (another thread) so keep waiting
4259
while True:
4360
time.sleep(64 * 64 * 100)

0 commit comments

Comments
 (0)