第1章互聯(lián)網(wǎng)常見架構:
C/S:客戶端和服務端
常見:wechat/qq
B/S:瀏覽器和服務器
常見:所有瀏覽器都是BS架構
第2章什么是socket?
Socket就是一系列接口,把傳輸層一下的協(xié)議都封裝成了簡單的接口
2.1為什么要用套接字?
目的是要編寫一個CS架構的軟件
server端必須具備的特點:
1.一直對外服務
2.必須綁定一個固定的地址
3.支持并發(fā)
2.2套接字的類型:
1.基于文件類型的套接字:AF_UNIX
兩個文件同時位于一個機器上,則可以共用一個文件系統(tǒng)來進行通信
2.基于網(wǎng)絡類型的套接字:AF_INET
2.3套接字的工作流程:
先從服務端說起,服務端先初始化socket,然后與端口綁定,對端口進行監(jiān)聽,調(diào)用accept阻塞,等待客戶端連接,在這時如果有個客戶端初始化一個socket,然后連接服務器connect,如果連接成功,這時客戶端與服務端的連接就建立了,客戶端發(fā)送數(shù)據(jù)請求,服務端接受請求并處理請求,然后把數(shù)據(jù)發(fā)送給客戶端,客戶端讀取數(shù)據(jù),最后關閉連接,一次交互結束
2.4關于套接字的方法:
服務端套接字函數(shù):
s.bind()綁定(主機,端口號)到套接字
s.listen()開始TCP監(jiān)聽
s.accept()被動接受TCP客戶的連接,(阻塞式)等待連接的到來
客戶端套接字函數(shù):
s.connect()主動初始化TCP服務器連接
s.connect_ex() connect()函數(shù)的擴展版本,出錯時返回出錯碼,而不是拋出異常
公共用途的套接字函數(shù):
s.recv()接收TCP數(shù)據(jù)
s.send()發(fā)送TCP數(shù)據(jù)(send在待發(fā)送數(shù)據(jù)量大于己端緩存區(qū)剩余空間時,數(shù)據(jù)丟失,不會發(fā)完)
s.sendall()發(fā)送完整的TCP數(shù)據(jù)(本質就是循環(huán)調(diào)用send,sendall在待發(fā)送數(shù)據(jù)量大于己端緩存區(qū)剩余空間時,數(shù)據(jù)不丟失,循環(huán)調(diào)用send直到發(fā)完)
s.recvfrom()接收UDP數(shù)據(jù)
s.sendto()發(fā)送UDP數(shù)據(jù)
s.getpeername()連接到當前套接字的遠端的地址
s.getsockname()當前套接字的地址
s.getsockopt()返回指定套接字的參數(shù)
s.setsockopt()設置指定套接字的參數(shù)
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('來自服務端的數(shù)據(jù):',data)
client.close()
2.6通信循環(huán)問題
服務端:
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循環(huán)鏈接問題
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有粘包現(xiàn)象,UDP則永遠沒有
3.1什么是粘包?
就是接受方不知道消息之間的界限,不知道一次性提取多少字節(jié)所造成的
3.2解決辦法:
問題的根源在于,接受端不知大發(fā)送端將要傳送的字節(jié)流的長度,所以解決粘包的方法就是圍繞,如何讓發(fā)送端在發(fā)送數(shù)據(jù)前把自己將要發(fā)送的字節(jié)流總大小讓接收端知曉,然后接收端來一個死循環(huán)接受所有數(shù)據(jù)即可
解決粘包問題服務端:
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()
#發(fā)送固定長度的報頭
total_size=len(stdout) +len(stderr)
conn.send(struct.pack('i',total_size))
#真實數(shù)據(jù)
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'))
#接受數(shù)據(jù)長度
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()