- 實例一:最簡單的socket例子
這個程序矮锈,實現(xiàn)了客戶端向服務端發(fā)送數(shù)據(jù)铐伴,服務端將小寫字母變?yōu)榇髮懖⑶曳祷?/strong>
服務端
import socket
server=socket.socket() #默認ipv4 tcp/ip 相當于聲明socket類型同時生成socket連接對象 既定義了地址簇,又定義了協(xié)議類型
server.bind(('localhost',1010) ) #綁定要監(jiān)聽的端口,只接受一個參數(shù)曼月,是一個元組
server.listen() #開始監(jiān)聽
print('我要開始等電話了!') #但是這個地方還沒有真正開始等衙猪,只是做好了準備工作
conn,addr=server.accept() #等電話打進來馍乙,真正的開始等電話打進來
print(conn,addr) #conn就是客戶端連過來而在服務器端為其生成的一個連接實例,也就是標記垫释,后面就是對方的地址
print("電話來了丝格!")
data=conn.recv(1024) #使用實例接受,發(fā)送
print(data)
conn.send(data.upper()) #發(fā)過去變成大寫
server.close() #關(guān)閉手機 所以是server
# server.accept()
# data=server.recv(1024)
# print('recv ',data)
# server.send(data.upper())
# server.close()
#TypeError: a bytes-like object is required, not 'str'
#不能直接使用server去接聽 server.accept
#這里就需要想手機切換通話棵譬,只是一根電話線显蝌,讓別的人通過這一根電話線打進來切換接聽另一個人
#所以用該給每個人標記一下 conn,addr=server.accept() 返回第一個是連接的標記位,第二個是對方的地址
結(jié)果:
F:\anaconda\python.exe F:/web/s14/網(wǎng)絡編程订咸、socket編程/socket_server.py
我要開始等電話了曼尊!
<socket.socket fd=272, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 1010), raddr=('127.0.0.1', 49866)> ('127.0.0.1', 49866)
電話來了!
b'hello world!'
客戶端
import socket
client=socket.socket() #默認ipv4 tcp/ip 相當于聲明socket類型同時生成socket連接對象
client.connect(('localhost',1010))
client.send(b"hello world!") # 不加b會出現(xiàn)這個錯誤 : TypeError: a bytes-like object is required, not 'str' 在python2里面可以發(fā)字符串脏嚷、字節(jié)bytes
#但是在python里只能發(fā)比特流的bytes類型
data=client.recv(1024) #收1024字節(jié)的數(shù)據(jù) 1kb
print(data)
client.close()
結(jié)果:
F:\anaconda\python.exe F:/web/s14/網(wǎng)絡編程骆撇、socket編程/socket_client.py
b'HELLO WORLD!'
- 實例二:實現(xiàn)服務器和客戶端重復發(fā)送和接受多次
客戶端
import socket
client=socket.socket()
client.connect(('localhost',1010))
while True:
msg=input('>>:').strip()
if len(msg) == 0:continue
client.send(msg.encode("utf-8"))
data=client.recv(1024)
print(data.decode())
client.close()
服務端
import socket
server=socket.socket()
server.bind(('localhost',1010) )
server.listen(5)
while True: #如果沒有這個while循環(huán)那么一旦客戶端關(guān)閉連接,在linux父叙、mac系統(tǒng)上神郊,服務端一直收的都是空,陷入一個死循環(huán)趾唱。而在windows上就會報錯涌乳。
conn,addr=server.accept() #必須放在外面,如果放在里面,可以同時接兩個電話甜癞,但是只能說一句
while True:
data=conn.recv(1024)
if not data:
print("client lost!")
break
print(data.decode())
conn.send(data.upper())
server.close()
- 實例三:socket實現(xiàn)簡單ssh功能(處理大數(shù)據(jù))
客戶端
import socket
client=socket.socket()
client.connect(('localhost',9999))
while True:
cmd = input(">>:").strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))
cmd_res_size=client.recv(1024) #接受命令返回結(jié)果的長度
print("命令長度: ",cmd_res_size)
recv_data = b''
recv_size = 0
while recv_size < int(cmd_res_size.decode()):
data=client.recv(1024)
recv_size += len(data) #每次收到的結(jié)果很可能小于1024 所以這里必須用len判斷
print(recv_size)
recv_data+=data
else:
print("cmd res recive done!",recv_size)
print(recv_data.decode())
client.close()
服務端
import socket
import os
server=socket.socket()
server.bind(('localhost',9999))
server.listen()
while True:
conn,addr=server.accept()
while True:
print("等待新指令")
data=conn.recv(1024)
if not data:
print("客戶端已斷開夕晓!")
break
print("執(zhí)行指令:",data)
cmd_res=os.popen(data.decode()).read() #接受字符串,執(zhí)行結(jié)果也是字符串
if len(cmd_res) == 0:
cmd_res='cmd has no output!'
#conn.send(str(len(cmd_res)).encode('utf-8'))
conn.send(str(len(cmd_res.encode())).encode("utf-8")) # 整數(shù)不能直接encode得先str 先把大小發(fā)給客戶端
conn.send(cmd_res.encode('utf-8'))
server.close()
服務端
import socket
import os,hashlib
server=socket.socket()
server.bind(('localhost',9999))
server.listen()
while True:
conn,addr=server.accept()
while True:
print("等待新指令")
data=conn.recv(1024)
if not data:
print("客戶端已斷開带欢!")
break
cmd,filename=data.decode().split()
if os.path.isfile(filename):
f=open(filename,'rb')
m=hashlib.md5()
file_size=os.stat(filename).st_size
conn.send(str(file_size).encode('utf-8')) #發(fā)送文件大小
conn.recv(1024) #wait for ack
for line in f:
m.update(line)
conn.send(line)
f.close()
conn.send(m.hexdigest().encode()) #send md5
print(m.hexdigest())
print('send done!')
server.close()
客戶端
import socket,hashlib
client=socket.socket()
client.connect(('localhost',9999))
while True:
cmd = input(">>:").strip()
if len(cmd) == 0:continue
if cmd.startswith("get"):
client.send(cmd.encode())
server_response=client.recv(1024)
print("文件大小",server_response)
client.send("ready to recv file".encode()) #ack
file_total_size = int(server_response.decode())
rece_size=0
filename=cmd.split()[1]
f=open(filename + '.new',"wb")
m=hashlib.md5()
while rece_size < file_total_size: #解決了粘包运授,有可能大于烤惊,就把MD5粘過來了
if file_total_size-rece_size>1024: #代表要收不止一次
size=1024
else: #最后一次,剩多少吁朦,收多少
size=file_total_size-rece_size
data=client.recv(size)
rece_size +=len(data)
m.update(data)
f.write(data)
else:
new_file_md5=m.hexdigest()
print(rece_size,file_total_size)
f.close()
server_file_md5 = client.recv(1024)
print("server file md5", server_file_md5.decode())
print("client file md5", new_file_md5)
client.close()
md5校驗
import hashlib
m=hashlib.md5()
m.update(b"test")
m.update(b'abc')
print(m.hexdigest())
m2=hashlib.md5()
m2.update(b'testabc')
print(m2.hexdigest())
#證明了逐行 md5 和一起md5的效果是一樣的
- 實例五:通過socketserver實現(xiàn)并發(fā)處理
服務端
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self): #默認在父類里是空的柒室,客戶端所有的交互都是在handle里完成的
while True:
try:
self.data=self.request.recv(1024).strip() #ConnectionResetError
print("{} wrote: ".format(self.client_address[0])) #打印客戶端的ip地址
print(self.data)
# if not self.data: #代表客戶端斷開
# print(self.client_address,"斷開了")
# break #在使用socket時候,windows上關(guān)閉客戶端會報錯逗宜,linux'會陷入無限循環(huán)而不報錯
#所以 需判斷
#而在socketserver上widows和linnux上斷開客戶端都會報ConnectionResetError
#所以使用異常處理
self.request.send(self.data.upper())
except ConnectionResetError as e:
print("err",e)
break
#每一個客戶端的請求過來都會實例化我們新創(chuàng)建的類
if __name__ == '__main__':
HOST,PORT = 'localhost', 6969
# server=socketserver.TCPServer((HOST,PORT),MyTCPHandler)
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) #多并發(fā)
server.serve_forever()
項目:實現(xiàn)ftp服務