第1章 互聯網常見架構:
shell
C/S:客戶端和服務端瀏覽器
常見:wechat/qq緩存
B/S:瀏覽器和服務器服務器
常見:全部瀏覽器都是BS架構網絡
Socket就是一系列接口,把傳輸層一下的協議都封裝成了簡單的接口架構
目的是要編寫一個CS架構的軟件併發
server端必須具有的特色:ssh
1. 一直對外服務socket
2. 必須綁定一個固定的地址tcp
3. 支持併發
1. 基於文件類型的套接字:AF_UNIX
兩個文件同時位於一個機器上,則能夠共用一個文件系統來進行通訊
2. 基於網絡類型的套接字:AF_INET
先從服務端提及,服務端先初始化socket,而後與端口綁定,對端口進行監聽,調用accept阻塞,等待客戶端鏈接,在這時若是有個客戶端初始化一個socket,而後鏈接服務器connect,若是鏈接成功,這時客戶端與服務端的鏈接就創建了,客戶端發送數據請求,服務端接受請求並處理請求,而後把數據發送給客戶端,客戶端讀取數據,最後關閉鏈接,一次交互結束
服務端套接字函數:
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() 獲得阻塞套接字操做的超時時間
服務端:
import socket
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()
客戶端:
import socket
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()
服務端:
import socket
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()
客戶端:
import socket
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()
import socket
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())
exceptConnectionRefusedError as e:
break
conn.close()
服務端:
import socket
import subprocess
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)
exceptConnectionRefusedError as e:
break
conn.close()
server.close()
客戶端:
import socket
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()
要知道:只有tcp有粘包現象,UDP則永遠沒有
就是接受方不知道消息之間的界限,不知道一次性提取多少字節所形成的
問題的根源在於,接受端不知大發送端將要傳送的字節流的長度,因此解決粘包的方法就是圍繞,如何讓發送端在發送數據前把本身將要發送的字節流總大小讓接收端知曉,而後接收端來一個死循環接受全部數據便可
解決粘包問題服務端:
import socket
import struct
import subprocess
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)
exceptConnectionRefusedError as e:
break
conn.close()
server.close()
客戶端:
import socket
import struct
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()
服務端:
import socket
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()
客戶端:
import socket
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()