第1章互聯網常見架構:
C/S:客戶端和服務端
常見:wechat/qq
B/S:瀏覽器和服務器
常見:所有瀏覽器都是BS架構
第2章什么是socket?
Socket就是一系列接口,把傳輸層一下的協議都封裝成了簡單的接口
2.1為什么要用套接字?
目的是要編寫一個CS架構的軟件
server端必須具備的特點:
1.一直對外服務
2.必須綁定一個固定的地址
3.支持并發
2.2套接字的類型:
1.基于文件類型的套接字:AF_UNIX
兩個文件同時位于一個機器上,則可以共用一個文件系統來進行通信
2.基于網絡類型的套接字:AF_INET
2.3套接字的工作流程:
先從服務端說起,服務端先初始化socket,然后與端口綁定,對端口進行監聽,調用accept阻塞,等待客戶端連接,在這時如果有個客戶端初始化一個socket,然后連接服務器connect,如果連接成功,這時客戶端與服務端的連接就建立了,客戶端發送數據請求,服務端接受請求并處理請求,然后把數據發送給客戶端,客戶端讀取數據,最后關閉連接,一次交互結束
2.4關于套接字的方法:
服務端套接字函數:
s.bind()綁定(主機,端口號)到套接字
s.listen()開始TCP監聽
s.accept()被動接受TCP客戶的連接,(阻塞式)等待連接的到來
客戶端套接字函數:
s.connect()主動初始化TCP服務器連接
s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
公共用途的套接字函數:
s.recv()接收TCP數據
s.send()發送TCP數據(send在待發送數據量大于己端緩存區剩余空間時,數據丟失,不會發完)
s.sendall()發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大于己端緩存區剩余空間時,數據不丟失,循環調用send直到發完)
s.recvfrom()接收UDP數據
s.sendto()發送UDP數據
s.getpeername()連接到當前套接字的遠端的地址
s.getsockname()當前套接字的地址
s.getsockopt()返回指定套接字的參數
s.setsockopt()設置指定套接字的參數
s.close()關閉套接字
面向鎖的套接字方法:
s.setblocking()設置套接字的阻塞與非阻塞模式
s.settimeout()設置阻塞套接字操作的超時時間
s.gettimeout()得到阻塞套接字操作的超時時間
2.5基于tcp的socket通信
服務端:
importsocket
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(3)
10
print('來自客戶端的請求')
print(addr)
data=conn.recv(1024)
print('來自客戶端的消息:',data)
conn.send(data.upper())
conn.close()
客戶端:
importsocket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8080))
client.send(bytes('nihao',encoding='utf-8'))
data=client.recv(1024)
print('來自服務端的數據:',data)
client.close()
2.6通信循環問題
服務端:
importsocket
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(3)
conn,addr=server.accept()
print(addr)
while True:
data=conn.recv(1024)
iflen(data) ==0:break
print('來自客戶端的消息:',data)
conn.send(data.upper())
conn.close()
客戶端:
importsocket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
iflen(msg) ==0:continue
client.send(bytes(msg,encoding='utf-8'))
data=client.recv(1024)
print(data)
client.close()
2.7循環鏈接問題
importsocket
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,addr=server.accept()
while True:
try:
data=conn.recv(1024)
iflen(data) ==0:break
print(data)
conn.send(data.upper())
exceptConnectionRefusedErrorase:
break
conn.close()
2.8tcp版ssh客戶端
服務端:
importsocket
importsubprocess
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,addr=server.accept()
while True:
try:
data=conn.recv(1024)
iflen(data) ==0:break
obj=subprocess.Popen(data.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
conn.send(stdout+stderr)
exceptConnectionRefusedErrorase:
break
conn.close()
server.close()
客戶端:
importsocket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
iflen(msg) ==0:continue
client.send(bytes(msg,encoding='utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
client.close()
第3章粘包問題
要知道:只有tcp有粘包現象,UDP則永遠沒有
3.1什么是粘包?
就是接受方不知道消息之間的界限,不知道一次性提取多少字節所造成的
3.2解決辦法:
問題的根源在于,接受端不知大發送端將要傳送的字節流的長度,所以解決粘包的方法就是圍繞,如何讓發送端在發送數據前把自己將要發送的字節流總大小讓接收端知曉,然后接收端來一個死循環接受所有數據即可
解決粘包問題服務端:
importsocket
importstruct
importsubprocess
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,addr=server.accept()
while True:
try:
data=conn.recv(1024)
iflen(data) ==0:break
obj=subprocess.Popen(data.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
#發送固定長度的報頭
total_size=len(stdout) +len(stderr)
conn.send(struct.pack('i',total_size))
#真實數據
conn.send(stdout+stderr)
exceptConnectionRefusedErrorase:
break
conn.close()
server.close()
客戶端:
importsocket
importstruct
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
iflen(msg) ==0:continue
client.send(bytes(msg,encoding='utf-8'))
#接受數據長度
header=client.recv(4)
total_size=struct.unpack('i',header)[0]
recv_size=0
res=b''
whilerecv_size < total_size:
recv_data=client.recv(1024)
res+=recv_data
recv_size+=len(recv_data)
print(res.decode('utf-8'))
client.close()
第4章一個簡單的基于UDP的socket客戶端和服務端
服務端:
importsocket
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080))
while True:
data,client_addr=server.recvfrom(1024)
print('===>',data,client_addr)
server.sendto(data.upper(),client_addr)
server.close()
客戶端:
importsocket
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
msg=input('>>: ').strip()
client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
data,server_addr=client.recvfrom(1024)
print(data)
client.close()
站長資訊網