基於TCP套接字
先從服務器端說起。服務器端先初始化Socket,然後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。在這時如果有個客戶端初始化一個Socket,然後連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了。客戶端發送數據請求,服務器端接收請求並處理請求,然後把迴應數據發送給客戶端,客戶端讀取數據,最後關閉連接,一次交互結束
tcp是基於鏈接的,必須先啓動服務端,然後再啓動客戶端去鏈接服務端
鏈接過程:三次握手————通信——————四次揮手
TCP服務端
from socket import *
ip_port=('218.197.223.92',800)#本機服務器的地址與端口
back_log=5 #半鏈池大小
buffer_size=1024 #接收字節數
tcp_serve=socket(AF_INET,SOCK_STREAM)#初始化socket,AF_INET是基於網絡模型的套接字家族,SOCK_STREAM是數據流模式
tcp_serve.bind(ip_port) #綁定IP與端口
tcp_serve.listen(back_log) #開始監聽
#可以接收多個客戶端
while True:
print('開始連接...')
con,addr=tcp_serve.accept() #連接成功後會返回鏈接與客戶端地址
#print(con,addr)
print('連接成功,客戶端地址爲%s'%(addr[0]))
#與一個客戶端持續通信
while True:
try:
msg=con.recv(buffer_size) #recv()讀取客戶端發送的信息,參數爲接收的字節數
print('接收的信息爲',msg.decode('utf-8'))
send_msg=input('>>:').strip()
if not send_msg:continue #當發送的信息爲空時無法傳輸,造成客戶端無法讀取,導致阻塞
con.send(send_msg.encode('utf-8')) #send()發送信息,必須爲二進制
except Exception: #當客戶端突然斷開時會發送錯誤,用異常處理捕捉錯誤,讓服務器繼續運行
print('客戶端%s連接斷開'%addr[0])
break
tcp_serve.close() #關閉服務器
TCP客戶端from socket import *
ip_port=('218.197.223.92',800) #要連接的服務器的IP與端口
buffer_size=1024 #要讀取的字節數
tcp_client=socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port) #連接服務器
#與服務器持續對話
while True:
msg=input('>>:').strip()
if not msg:continue #當msg爲空時,數據不會發送出去,服務器也就收不到信息,會導致程序阻塞
tcp_client.send(msg.encode('utf-8'))
msg=tcp_client.recv(buffer_size)
print('收到的信息爲',msg.decode('utf-8'))
tcp_client.close() #關閉客戶端
遠程執行命令的TCP服務端
from socket import *
import subprocess
'''遠程執行命令'''
ip_port=('218.197.223.92',800)
back_log=5
buffer_size=1024
tcp_shell_serve=socket(AF_INET,SOCK_STREAM)
tcp_shell_serve.bind(ip_port)
tcp_shell_serve.listen(back_log)
while True:
print('開始連接...')
conn,addr=tcp_shell_serve.accept()
print('連接到客戶端:%s'%addr[0])
while True:
try:
cmd=conn.recv(buffer_size)
if not cmd:break #當客戶端正常斷開時傳入爲空
res=subprocess.Popen(cmd.decode('utf-8'),shell=True, #運行命令提示符並執行指令 ,並把輸出,異常和輸入存在subprocess的管道(PIPE)中
stdout=subprocess.PIPE, #輸出
stdin=subprocess.PIPE, #輸入
stderr=subprocess.PIPE) #異常
err=res.stderr.read() #由管道讀出異常
if err: #若存在異常則返回異常
res_cmd=err
else:
res_cmd=res.stdout.read() #否則返回輸出結果(輸出結果爲二進制,編碼方式由系統決定,Windowns爲gbk編碼)
conn.send(res_cmd) #把結果發送給客戶端
except Exception: #當客戶端非正常斷開時會觸發異常
print('客戶端%s斷開連接'%addr[0])
break
tcp_shell_serve.close()
基於UDP套接字
udp是無鏈接的,先啓動哪一端都不會報錯(由於udp無連接,所以可以同時多個客戶端去跟服務端通信)
UDP服務端from socket import *
ip_port=('218.197.223.92',800)
buffer_size=1024
udp_serve=socket(AF_INET,SOCK_DGRAM)#創建基於UDP的服務器套接字,SOCK_DGRAM爲數據報模式
udp_serve.bind(ip_port) #綁定主機地址與端口
#因爲UDP是無連接的,所以不需要TCP中的監聽(listen)與鏈接(accpet)
#與客戶端持續通信
while True:
data,addr=udp_serve.recvfrom(buffer_size) #recvfrom()讀取客戶端發送的信息,返回信息與客戶端地址
print('來着客戶端地址%s的信息:%s'%(addr,data.decode('utf-8')))
msg=input('>>:').strip() #UDP空字符不會導致阻塞
udp_serve.sendto(msg.encode('utf-8'),addr)#sendto()向客戶端發送信息,包括要發送的信息與客戶端地址
udp_serve.close()#關閉服務器
UDP客戶端from socket import *
ip_port=('218.197.223.92',800)
buffer_size=1024
udp_client=socket(AF_INET,SOCK_DGRAM)
while True:
msg=input('>>:').strip()
udp_client.sendto(msg.encode('utf-8'),ip_port)
data,addr=udp_client.recvfrom(buffer_size)
print('來着服務端的消息:%s'%(data.decode('utf-8')))
udp_client.close()
模擬一個向服務器發送請求返回時間的UDP服務器
from socket import *
import time
ip_port=('218.197.223.92',800)
buffer_size=1024
udp_serve=socket(AF_INET,SOCK_DGRAM)#創建基於UDP的服務器套接字,SOCK_DGRAM爲數據報模式
udp_serve.bind(ip_port) #綁定主機地址與端口
#因爲UDP是無連接的,所以不需要TCP中的監聽(listen)與鏈接(accpet)
#與客戶端持續通信
while True:
data,addr=udp_serve.recvfrom(buffer_size) #recvfrom()讀取客戶端發送的信息,返回信息與客戶端地址
if not data:
fmt='%Y-%m-%d %X'
else:
fmt=data.decode('utf-8')
udp_serve.sendto(time.strftime(fmt).encode('utf-8'),addr)#sendto()向客戶端發送信息,包括要發送的信息與客戶端地址
udp_serve.close()#關閉服務器
鏈接合法驗證
服務端
from socket import *
import os,hmac
ip_port=('10.11.165.103',800)
secret_key=b'hello world' #鹽
def conn_auth(conn,secret_key):
print('開始驗證鏈接的合法性')
data=os.urandom(32) #產生隨機的32位二進制
conn.sendall(data)
h=hmac.new(secret_key,data) #進行哈希運算(加鹽)
digest=h.digest()
rec=conn.recv(len(digest))
res=hmac.compare_digest(digest,rec)
if res:
conn.send('1'.encode('utf-8'))
else:
conn.send('0'.encode('utf-8'))
return res
def requst_handler(conn,buffer_size=1024):
if not conn_auth(conn,secret_key):
print('該鏈接不合法,關閉鏈接')
conn.close()
return
print('鏈接驗證成功,開始通信')
while True:
data=conn.recv(buffer_size)
print('收到的消息爲:',data.decode('utf-8'))
conn.send(data.upper())
def serve_handler(ip_port,back_log=5):
s=socket(AF_INET,SOCK_STREAM)
s.bind(ip_port)
s.listen(back_log)
conn,addr=s.accept()
return requst_handler(conn)
if __name__=='__main__':
serve_handler(ip_port)
客戶端from socket import *
import hmac
ip_port=('10.11.165.103',800)
secret_key=b'hello world'
def auth_conn(conn):
print('開始驗證客戶端到服務端的鏈接')
data=conn.recv(32)
h=hmac.new(secret_key,data)
digest=h.digest()
conn.sendall(digest)
res=conn.recv(32)
auth=bool(int(res.decode('utf-8')))
return auth
def data_handler(ip_port,buffer_size=1024):
s=socket(AF_INET,SOCK_STREAM)
s.connect(ip_port)
if not auth_conn(s):
print('驗證失敗')
return
print('客戶端鏈接到服務端成功')
while True:
msg=input('>>:').strip()
if not msg:continue
s.send(msg.encode('utf-8'))
data=s.recv(buffer_size)
print('收到的消息是',data.decode('utf-8'))
if __name__=='__main__':
data_handler(ip_port)