版本:Python3
知識(shí)點(diǎn):Socket,threading
一、服務(wù)器端
1作郭,服務(wù)器綁定地址與端口號(hào)陨囊,保持監(jiān)聽(tīng)狀態(tài)。
2夹攒,無(wú)限循環(huán)接收客戶端的連接蜘醋,驗(yàn)證身份判斷是否是客戶端,如果不是立即關(guān)閉連接咏尝,如果是則創(chuàng)建一個(gè)新線程管理對(duì)客戶端的所有操作压语,并將其加入到連接池。
3编检,當(dāng)客戶端主動(dòng)終止連接時(shí)胎食,通知其他用戶其已離開(kāi)聊天室,關(guān)閉連接允懂,從連接池里將其剔除厕怜。
運(yùn)行演示.png
代碼:
# encoding: utf-8
# Author: Timeashore
# Time: 2017-12-31
# Email: 1274866364@qq.com
# 服務(wù)器端
import threading
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1',5000))
s.listen(5)
print('Server',socket.gethostbyname('127.0.0.1'),'listening ...')
mydict = dict() # fileno:nickname
mylist = list() # 連接池列表
# 向在線人發(fā)送消息(除了發(fā)消息本人)
def send_everybody(number, message):
for user in mylist:
if user.fileno() != number:
user.send(message.encode())
# 每個(gè)線程的調(diào)用方法,管理對(duì)客戶端的所有操作
def fun(conn, number):
mylist.append(conn) # 添加到連接池
nickname = conn.recv(1024).decode()
conn.send(('Hello,'+nickname).encode())
mydict[number] = nickname # 添加到fileno:nickname
print('connection',number,'has nickname : ',nickname) # 打印鏈接信息
send_everybody(number, '【系統(tǒng)提示:'+nickname+' 進(jìn)入聊天室】')
while True:
try:
conn.send(''.encode())
getmessage = conn.recv(1024).decode()
if getmessage:
print(nickname,':',getmessage)
send_everybody(number,nickname+': '+getmessage)
except (OSError, ConnectionResetError):
try:
mylist.remove(conn) # 從連接池列表剔除
except:
pass
print(nickname, 'exit, ', len(mylist), ' person left') # 服務(wù)器終端打印離開(kāi)信息
send_everybody(number, '【系統(tǒng)提示:' + nickname + ' 離開(kāi)聊天室】') # 告知所有人nickname已經(jīng)離開(kāi)聊天室
conn.close() # 關(guān)閉連接
return
while True:
conn, addr = s.accept()
print('Accept new connection',conn.getsockname(),conn.fileno())
try:
verification = conn.recv(1024).decode()
if verification == 'user':
conn.send('welcome to server!\n'.encode())
conn.send('nickname:\n'.encode())
t = threading.Thread(target=fun, args=(conn, conn.fileno()))
t.setDaemon(True)
t.start()
else:
conn.send('Your verification NOT pass'.encode())
conn.close()
except:
pass
二蕾总、客戶端
與服務(wù)器端相比粥航,客戶端比較簡(jiǎn)單。
1谤专,與服務(wù)器建立連接躁锡,發(fā)送身份驗(yàn)證信息。
2置侍,啟動(dòng)兩個(gè)線程映之,一個(gè)發(fā)送消息,一個(gè)接收消息蜡坊。
用戶1.png
用戶2.png
代碼:
# encoding: utf-8
# Author: Timeashore
# Time: 2017-12-31
# Email: 1274866364@qq.com
import socket
import threading
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',5000))
s.send('user'.encode()) # 驗(yàn)證身份
print(s.recv(1024).decode()) # 接受歡迎消息
print(s.recv(1024).decode()) # 接受歡迎消息
nickname = input()
s.send(nickname.encode())
# 接收消息
def recv():
while True:
try:
message = s.recv(1024).decode()
if message:
print(message)
else:
pass
except ConnectionAbortedError:
print('Server closed this connection!')
except ConnectionResetError:
print('Server is closed!')
# 發(fā)送消息
def send():
while True:
try:
ready_send = input('')
s.send(ready_send.encode())
except ConnectionAbortedError:
print('Server closed this connection!')
except ConnectionResetError:
print('Server is closed!')
# 啟動(dòng)兩個(gè)線程杠输,一個(gè)發(fā)送一個(gè)接收
t1 = threading.Thread(target=recv)
t2 = threading.Thread(target=send)
for x in [t1,t2]:
x.setDaemon(True)
x.start()
t1.join()
t2.join()
以上是在終端運(yùn)行,可以使用Python GUI工具 Tkinter 給客戶端增加一個(gè)界面秕衙,客戶端收到的消息實(shí)時(shí)顯示在界面上蠢甲,需要處理線程和GUI的配合。
GUI客戶端.png