網路程式設計:fork多程序網路通訊應用例項--聊天室(群聊)
簡單的群聊聊天室
1、功能
類似於qq群聊
【1】進入聊天室需要輸入姓名, 姓名不能重複
【2】有人進入聊天室此時會向其他人發起通知 ,xxx 進入了聊天室
【3】如果一個人發訊息,則其他人都能收到 ,xxx 說 : xxxxxxx
【4】如果某個人退出聊天室其他人也會收到通知, xxx 退出了聊天室
【5】服務端可以喊話 :此時群裡所有人都能收到服務端訊息 。管理員 說:xxx
2、功能分析
整體結構
Q:分為幾部分,如何封裝,使用什麼樣的技術手段
A:服務端 ,客戶端, 在客戶端和服務端將每個功能封裝為一個函式
3、技術方案
【1】轉發
一個客戶端傳送給伺服器,伺服器傳送給其他人
【2】套接字使用
udp 完成操作
【3】使用者儲存
字典 或者 列表 (可變型別,能夠遍歷提取)
【4】地址 使用者名稱
【5】傳送和接受訊息的控制
傳送和接收使用多程序分離互不影響
4、注意事項
【1】注重封裝
【2】分段測試
5、程式碼編寫流程
搭建通訊 --> 建立多程序 --->每個程序功能確定 --> 實現每一個功能模組
6、具體細節梳理
【1】進入聊天室
①客戶端
① 輸入姓名, 將資訊傳送給伺服器,adress,name
②接受到服務端返回結果判斷下一步執行什麼
②服務端
①接受訊息,判斷請求型別
②判斷是否可以登入(姓名是否已經存在)
③返回給客戶端是否登入(如果可以服務端會將姓名插入到儲存使用者資訊的資料結構中)
④給所有人傳送訊息
【2】聊天
① 客戶端
① 發起聊天:name ,msg
②接受伺服器回覆
②服務端
①接受訊息 ,判斷訊息型別
②組織訊息結構轉發給其他客戶端
【3】退出聊天室
① 客戶端
① 傳送訊息退出:Q,name
② 接收服務端回覆
③ 退出程式
② 服務端
① 接收訊息
② 判斷請求型別
③ 從儲存使用者資訊的資料結構中刪除對應使用者
④ 告知所有人,xxx退出
【fork-socket-groupChat-server.py】
from socket import *
import os,sys
#傳送管理員訊息
def do_child(s,addr):
while True:
msg = input("管理員訊息:")
msg = "C 管理員 " + msg
s.sendto(msg.encode(),addr)
#使用者登入
def do_login(s,user,name,addr):
if (name in user) or name == "管理員":
s.sendto("該使用者已存在".encode(),addr)
return
s.sendto(b'OK',addr)
#通知所有人
msg = "\n歡迎 %s 進入聊天室"%name
for i in user:
s.sendto(msg.encode(),user[i])
#插入user
user[name] = addr
def do_chat(s,user,name,data):
msg = "\n{} 說: {}".format(name,data)
for i in user:
if i != name:
s.sendto(msg.encode(),user[i])
def do_quit(s,user,name):
msg = "\n%s 離開了聊天室"%name
for i in user:
if i == name:
s.sendto(b'EXIT',user[i])
else:
s.sendto(msg.encode(),user[i])
del user[name] #刪除離開的使用者
#接收客戶端請求並處理
def do_parent(s):
# 用於儲存使用者 {'Alex':('127.0.0.1',8888)}
user = {}
while True:
msg,addr = s.recvfrom(1024)
msgList = msg.decode().split(' ')
if msgList[0] == 'L':
do_login(s,user,msgList[1],addr)
elif msgList[0] == 'C':
# "C Levi [I miss you]"
data = ' '.join(msgList[2:])
do_chat(s,user,msgList[1],data)
elif msgList[0] == 'Q':
do_quit(s,user,msgList[1])
# 建立套接字,網路連線,建立父子程序
def main():
#server address
ADDR = ('0.0.0.0',8888)
#建立套接字
s = socket(AF_INET,SOCK_DGRAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(ADDR)
#建立父子程序
pid = os.fork()
if pid < 0:
sys.exit("建立程序失敗")
elif pid == 0:
do_child(s,ADDR)
else:
do_parent(s)
if __name__ == "__main__":
main()
【fork-socket-groupChat-client.py】
from socket import *
import sys,os
def login(s,ADDR):
while True:
name = input("請輸入使用者名稱:")
msg = "L " + name
s.sendto(msg.encode(),ADDR)
#接收登入結果
data,addr = s.recvfrom(1024)
if data.decode() == 'OK':
print("@進入聊天室@")
return name
else:
print(data.decode())
#傳送訊息
def do_child(s,name,addr):
while True:
text = input("發言(quit退出):")
#退出
if text.strip() == "quit":
msg = "Q " + name
s.sendto(msg.encode(),addr)
sys.exit("退出聊天室")
msg = "C %s %s"%(name,text)
s.sendto(msg.encode(),addr)
#接收訊息
def do_parent(s):
while True:
msg,addr = s.recvfrom(1024)
if msg.decode() == 'EXIT':
sys.exit(0)
print(msg.decode()+"\n發言(quit退出):",end="")
#main控制套接字的建立
def main():
if len(sys.argv) < 3:
print("argv is error")
return
HOST = sys.argv[1]
PORT = int(sys.argv[2])
ADDR = (HOST,PORT)
s = socket(AF_INET,SOCK_DGRAM)
name = login(s,ADDR)
if name:
pid = os.fork()
if pid < 0:
sys.exit("建立子程序失敗")
elif pid == 0:
do_child(s,name,ADDR)
else:
do_parent(s)
else:
return
if __name__ == "__main__":
main()