socket網(wǎng)絡(luò)編程
- socket基于tcp酣藻、ip協(xié)議的工具
- 所有的網(wǎng)絡(luò)通信都基于socket
- wanghzh/p/5560787.html
- wupeiqi/articles/5040823.html
- 客戶端去訪問服務(wù)器:
- TCP:三次握手
- UDP:單向訪問
- python支持unix系統(tǒng)進(jìn)程與進(jìn)程之間的通信
自己實(shí)現(xiàn)socket客戶端和服務(wù)端
socket服務(wù)端
import socket
sk = socket.socket() # 服務(wù)器啟動(dòng),創(chuàng)建socket
ip_port = ('127.0.0.1', 9999)
sk.bind(ip_port) # socket綁定ip和端口作為服務(wù)端
sk.listen(5) # socket服務(wù)器支持連接等待的數(shù)量
while True:
sock, addr = sk.accept() # 阻塞等待客戶端訪問
sock.sendall(bytes('已鏈接到服務(wù)器', encoding='utf-8'))
print(type(sock))
while True:
try:
byte_data = sock.recv(1024) # 客戶端如果斷開了,這里會報(bào)異常
data = str(byte_data, encoding='utf-8')
ret = '服務(wù)器:%s' % data
sock.sendall(bytes(ret, encoding='utf-8'))
if data == 'q':
break
except Exception as e:
break
else:
pass
finally:
pass
socket客戶端
import socket
client = socket.socket() # 創(chuàng)建socket
client.connect(('127.0.0.1', 9999,)) # socket去連接服務(wù)端
byte_data = client.recv(1024)
print(str(byte_data, encoding='utf-8'))
while True:
inp = input()
client.sendall(bytes(inp, encoding='utf-8'))
byte_data = client.recv(1024)
data = str(byte_data, encoding='utf-8')
if data == '服務(wù)器:q':
print('斷開連接')
break
else:
print(data)
client.close()
- socket.sendall()方法在python2.7中接收字符串,在python3.x中接收ude是字節(jié)
socket更多方法
- wupeiqi/articles/5040823.html
sk = socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM,proto=0)
# family:socket.AF_INET = IPv4(默認(rèn)) IPv6
# type:TCP協(xié)議
# proto:默認(rèn)為0
sk.bind((ip,port,)
# 服務(wù)端綁定IP和port
sk.listen(count)
# 開始監(jiān)聽,參數(shù)表示最多等待的數(shù)量,當(dāng)count == 2
# 一個(gè)客戶端連上了服務(wù)器,如果再有2個(gè)客戶端訪問,后面的2個(gè)會處于等待狀態(tài),當(dāng)再有客戶端訪問,超過count == 2的客戶端會報(bào)錯(cuò)
sk.setblocking(bool)
# 是否阻塞
# True:阻塞(默認(rèn))
# False:不阻塞,當(dāng)socket調(diào)用recv和accept時(shí)會報(bào)錯(cuò),以后會用到
sk.connect((ip,port,)
# 客戶端連接服務(wù)端
sk.connect_ex((ip,port,)
# 同上,只是如果連接上,則返回0,如果連不上,返回錯(cuò)誤碼
sk.close()
# 斷開連接
sk.recv(1024)
# 接收信息,參數(shù)表示一次最所接收多少個(gè)字節(jié)
sk.recvfrom(buffersize)
# 從哪里接收UPD用到
sk.send()
# 發(fā)送信息,2.7中接收字符串,3.x中接收字節(jié)
# send方法有個(gè)弊端,實(shí)際發(fā)送的數(shù)據(jù)會出現(xiàn)少于實(shí)質(zhì)傳入的數(shù)據(jù)
sk.sendall()
# 底層通過循環(huán)和send方法發(fā)送數(shù)據(jù),不會出現(xiàn)數(shù)據(jù)丟失
sk.sendto()
# 針對UDP
sk.settimeout()
# 設(shè)置超時(shí)
sk.getsocketname()
# 返回socket自己的地址
sk.getpeername()
# 返回socket連接遠(yuǎn)程的地址
sk.fileno()
# socket的文件描述(后面會用到)
socket實(shí)現(xiàn)文件上傳
- 注意防止粘包問題
server
import socket
server = socket.socket()
server.bind(('127.0.0.1', 9999))
server.listen(5)
print('服務(wù)器啟動(dòng):\n>>>>>')
while True:
conn,address = server.accept()
print('獲取到客戶端訪問:ip:%s port:%s'%(address[0], address[1]))
retStr = '文件上傳服務(wù)器,請發(fā)送文件名以及文件大小'
conn.sendall(bytes(retStr, encoding='utf-8'))
fileInfo = conn.recv(1024)
fileInfo = str(fileInfo, encoding='utf-8')
fileName, fileLength = fileInfo.split('&')
print('獲取到文件名:%s 文件大谐晏搿:%s'%(fileName,fileLength))
conn.sendall(bytes('接收成功',encoding='utf-8'))
totalLength = int(fileLength)
hasLoadSize = 0
file = open(fileName, mode='wb')
while True:
if hasLoadSize >= totalLength:
break
data = conn.recv(1024)
file.write(data)
client
import socket
import os
client = socket.socket()
client.connect(('127.0.0.1', 9999))
connectInfo = client.recv(1024)
connectInfo = str(connectInfo, encoding='utf-8')
print(connectInfo)
fileName = input('請輸入文件名\n>>> ')
fileLength = os.stat('dest_top_img.jpg').st_size
ret_msg = '%s&%s'%(fileName,fileLength)
client.sendall(bytes(ret_msg, encoding='utf-8'))
rec_msg = client.recv(1024)
print(str(rec_msg, encoding='utf-8'))
print(str(rec_msg, encoding='utf-8'))
with open('dest_top_img.jpg', mode='rb') as file:
for line in file:
client.sendall(line)
client.close()
- 粘包原因:
- 報(bào)錯(cuò):ImageNotLoaded(下載完后打不開文件)
- 客戶端發(fā)送文件到服務(wù)端,需要依賴系統(tǒng)的緩沖區(qū)
- 服務(wù)端接收客戶端發(fā)送的文件,需要到緩沖區(qū)取
- 但是客戶端緩沖區(qū)什么時(shí)候發(fā)送是系統(tǒng)決定的,不可控,可能存在這種情況,當(dāng)發(fā)送文件名和文件大小給服務(wù)器的時(shí)候,數(shù)據(jù)存放在緩沖區(qū)還沒發(fā)送出直接發(fā)送文件內(nèi)容瓤檐,當(dāng)服務(wù)端獲取文件和文件大小的字段帶有一部分文件的內(nèi)容,這就導(dǎo)致需要發(fā)送的文件到服務(wù)器實(shí)際接收的時(shí)候不完整
- 解決:發(fā)送文件名拾氓、大小和發(fā)送文件之間與服務(wù)器做一個(gè)響應(yīng)
socketserver模塊實(shí)現(xiàn)多并發(fā)
- socket模塊不支持多并發(fā),服務(wù)端同一時(shí)刻只支持響應(yīng)一個(gè)客戶端請求
- 處理并發(fā)有兩種方式:
IO多路復(fù)用
-
socketserver模塊:由python提供的另一個(gè)模塊,用于解決多并發(fā)訪問的問題
- 內(nèi)部實(shí)現(xiàn):IO多路復(fù)用&多線程或多進(jìn)程并發(fā)操作
-
實(shí)現(xiàn)步驟:
- 創(chuàng)建類繼承:socketserver.BaseRequestHandler
- 創(chuàng)建server,傳入ip端口和剛創(chuàng)建的類
- 開啟服務(wù)
```
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
print(self.request) # 客戶端的連接
print(self.client_address) # 客戶端地址
print(self.server) # 當(dāng)前服務(wù)器:server
if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1',9999),MyServer)
server.serve_forever()
# <socket.socket fd=520, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 4971)>
# ('127.0.0.1', 4971)
# <socketocketserver.ThreadingTCPServer object at 0x00FAB310>
```
```
import socket
client = socket.socket()
client.connect(('127.0.0.1',9999))
```
梳理
- 對象中封裝對象
- 面向?qū)ο蟮膬煞N特性
- 裝飾器 + 方法
- @property + 方法
- python2.7繼承流程
- 繼承object類(新式類):頂部最后
- 未繼承object類(經(jīng)典類):一條道走到黑,深度優(yōu)先
- python3.x繼承流程
- 頂部最后
- 抽象類和抽象方法(python中目前用的不多)
- 約束
- 接口(python沒有接口)
- 抽象類+抽象方法