01.套接字
套接字用于應(yīng)用程序間的通信智厌,任何程序間的通信開(kāi)始之前都要?jiǎng)?chuàng)建套接字
有兩種類(lèi)型的套接字:基于文件的和面向網(wǎng)絡(luò)的饭望。
套接字最初是為同一主機(jī)上的應(yīng)用程序所創(chuàng)建灭返,使得主機(jī)上運(yùn)行的一個(gè)進(jìn)程和另一個(gè)進(jìn)程進(jìn)行通信汤徽,也就是進(jìn)程間通信(IPC Inter Process Communication)打肝。
文件套接字:
Unix套接字是套接字的第一個(gè)家族脂新,家族名AF_UNIX(又名AF_LOCAL),家族名代表地址家族(address family)
網(wǎng)絡(luò)套接字:
網(wǎng)絡(luò)套接字也有自己的家族名粗梭,AF_INET戏羽,另一個(gè)地址家族AF_INET6是用于ipv6尋址。還有一些其他的地址家族楼吃,但是目前使用最廣泛的是AF_INET
python支持AF_UNIX.AF_NETLINK,AF_TIPC,AF_INET幾個(gè)家族始花。對(duì)于網(wǎng)絡(luò)編程來(lái)說(shuō),我們將使用AF_INET.所以接下來(lái)討論的內(nèi)容都是AF_INET家族的套接字
套接字地址有主機(jī)號(hào)(ip)和端口號(hào)兩個(gè)部分組成孩锡,有效端口號(hào)的范圍是0~65535酷宵,小于1024的端口號(hào)是系統(tǒng)預(yù)留的,所以我們寫(xiě)程序的時(shí)候不要去占用躬窜。
02.面向連接的套接字和無(wú)連接的套接字
001.面向連接的套接字
面向連接的通信提供序列化的浇垦、可靠的和不重復(fù)的數(shù)據(jù)交付,沒(méi)有記錄邊界荣挨。每條消息可以拆成多個(gè)片段男韧,并且每一個(gè)消息片段都能確保到達(dá)目的地朴摊,然后將他們按順序組合在一起,最后將完整消息傳遞給正在等待的應(yīng)用程序此虑。
主要就是傳輸控制協(xié)議tcp甚纲。
為了創(chuàng)建tcp套接字,必須使用SOCK_STREAM作為套接字類(lèi)型朦前。
002.無(wú)連接的套接字
不需要建立連接介杆,也無(wú)法確保數(shù)據(jù)的順序性,可靠性和重復(fù)性韭寸。
可以類(lèi)比于郵政服務(wù)春哨,信件和包裹也許并不能以發(fā)送順序到達(dá),也可能根本沒(méi)有到達(dá)恩伺。
相對(duì)于面向連接服務(wù)赴背,沒(méi)有創(chuàng)建維持連接需要的成本,資源消耗更小晶渠,更快速凰荚。
主要是用戶(hù)數(shù)據(jù)報(bào)協(xié)議UDP
為了創(chuàng)建UDP協(xié)議的套接字,必須使用SOCK_DGRAM作為套接字類(lèi)型乱陡。
tcp和udp都是結(jié)合ip協(xié)議尋找互聯(lián)網(wǎng)上的主機(jī)。所以這種系統(tǒng)被稱(chēng)為T(mén)CP/IP,UDP/IP
03.python 中的網(wǎng)絡(luò)編程
socket.socket(socket_family,socket_type,protocol=0)
socket_family是AF_UNIX或AF_INET仪壮,socket_type是SOCK_STREAM或SOCK_DGRAM憨颠。protocol通常省略,默認(rèn)為0.
socket對(duì)象的其他方法接下來(lái)將通過(guò)實(shí)例來(lái)演示:
下面首先演示的是一個(gè)tcp時(shí)間戳服務(wù)器:
import socket
import time
HOST = ''
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST,PORT)
# 創(chuàng)建服務(wù)端套接字用于接受客戶(hù)端的連接
tcpSerSock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 綁定本地端口
tcpSerSock.bind(ADDR)
# 設(shè)定允許連接的最大數(shù)值
tcpSerSock.listen(5)
while True:
print('waiting for connection,local address:',ADDR)
# accept方法接受客戶(hù)端發(fā)送的請(qǐng)求积锅,并且產(chǎn)生一個(gè)服務(wù)客戶(hù)端用的socket爽彤,返回客戶(hù)端的地址
tcpClientSock,addr = tcpSerSock.accept()
print('...connected from:',addr)
while True:
# 建立連接后,用tcpClientSock服務(wù)客戶(hù)端缚陷,接受客戶(hù)端發(fā)送的數(shù)據(jù)适篙,使用指定大小的緩沖區(qū)
data = tcpClientSock.recv(BUFSIZE)
print('recieving data:',data)
# 當(dāng)沒(méi)有收到數(shù)據(jù)時(shí)退出循環(huán)
if not data:
break
# 在客戶(hù)端發(fā)送的數(shù)據(jù)前加上時(shí)間戳再送回,socket需要放送bytes類(lèi)型的數(shù)據(jù),所以這里b是轉(zhuǎn)化二進(jìn)制數(shù)據(jù)
tcpClientSock.send(b'[%s] %s'%(bytes(time.ctime(),'utf-8'),data))
# 關(guān)閉服務(wù)客戶(hù)端的socket
tcpClientSock.close()
# 關(guān)閉服務(wù)器的socket,現(xiàn)實(shí)情況下服務(wù)器都是一直運(yùn)行的,所以一般不會(huì)關(guān)閉
tcpSerSock.close()
接下來(lái)是創(chuàng)建tcp客戶(hù)端
創(chuàng)建客戶(hù)端比服務(wù)器簡(jiǎn)單一些箫爷,
下面創(chuàng)建的客戶(hù)端連接之前創(chuàng)建的服務(wù)器嚷节,向服務(wù)器發(fā)送數(shù)據(jù),并且打印服務(wù)器返回的數(shù)據(jù)虎锚。
import socket
HOST = 'localhost'
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST,PORT)
tcpCliSock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
data = input('> ')
if not data:
break
# socket需要放送bytes類(lèi)型的數(shù)據(jù)硫痰,所以這里encode是轉(zhuǎn)化二進(jìn)制數(shù)據(jù)
tcpCliSock.send(data.encode('utf8'))
data = tcpCliSock.recv(BUFSIZE)
if not data:
break
print(data)
tcpCliSock.close()
接下來(lái)是創(chuàng)建udp服務(wù)器。由于是無(wú)連接的窜护,所以并沒(méi)有為了成功而使一個(gè)客戶(hù)端連接到一個(gè)獨(dú)立的套接字的“轉(zhuǎn)換”操作效斑。
import socket
import time
HOST = ''
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST, PORT)
udpSerSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
udpSerSock.bind(ADDR)
while True:
print('waiting for message...')
data,addr = udpSerSock.recvfrom(BUFSIZE)
print('receiving data from %s,data:%s'%(addr,data))
sendstr='[%s] %s'%(time.ctime(),data)
udpSerSock.sendto(sendstr.encode('utf8'),addr)
print('...received from and returned to:',addr)
udpSerSock.close()
# 創(chuàng)建udp客戶(hù)端
import socket
import time
HOST = 'localhost'
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST, PORT)
udpCliSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
data = input('>')
if not data:
break
udpCliSock.sendto(data.encode('utf8'),ADDR)
data,addr = udpCliSock.recvfrom(BUFSIZE)
if not data:
break
print(data)
udpCliSock.close()
04.socketserver模塊
socketserver是python標(biāo)準(zhǔn)庫(kù)里面的一個(gè)高級(jí)模塊,目標(biāo)是簡(jiǎn)化創(chuàng)建客戶(hù)端和服務(wù)器的代碼柱徙。
下面我們先介紹tcp時(shí)間戳服務(wù)器的例子:
import socketserver
import time
HOST = ''
PORT = 21567
ADDR = (HOST,PORT)
class MyRequestHandler(socketserver.StreamRequestHandler):
def handle(self):
print('...connected from:',self.client_address)
self.wfile.write(('[%s] %s'%(time.ctime(),self.rfile.readline())).encode('utf8'))
tcpServ = socketserver.TCPServer(ADDR,MyRequestHandler)
print('waiting for connection...')
tcpServ.serve_forever()
客戶(hù)端
import socket
HOST = 'localhost'
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST,PORT)
while True:
tcpCliSock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcpCliSock.connect(ADDR)
data = input('> ')
if not data:
break
tcpCliSock.send(('%s\r\n'%data).encode('utf8'))
data = tcpCliSock.recv(BUFSIZE)
if not data:
break
print(data.strip())
tcpCliSock.close()
用這個(gè)高級(jí)模塊缓屠,自己帶一些異常處理奇昙,可以檢測(cè)到鍵盤(pán)中斷了。