Socket
TCP協(xié)議:
有請求 有響應 稱為TCP協(xié)議
是面向連接的協(xié)議,也就是說,在收發(fā)數(shù)據(jù)前,必須和對方建立可靠的連接。一個TCP連接必須要經(jīng)過三次“對話”才能建yc立起來,
如:網(wǎng)站請求
TCP三次握手的過程如下:
- 客戶端發(fā)送SYN(SEQ=x)報文給服務器端,進入SYN_SEND狀態(tài)。
- 服務器端收到SYN報文,回應一個SYN (SEQ=y)ACK(ACK=x+1)報文,進入SYN_RECV狀態(tài)。
- 客戶端收到服務器端的SYN報文,回應一個ACK(ACK=y+1)報文,進入Established狀態(tài)。
三次握手完成,TCP客戶端和服務器端成功地建立連接,可以開始傳輸數(shù)據(jù)了。
UDP協(xié)議:
? UDP是一個非連接的協(xié)議,傳輸數(shù)據(jù)之前源端和終端不建立連接
用到的應用:如飛秋 qq
一 什么是 Socket?
Socket又稱"套接字",應用程序通常通過"套接字"向網(wǎng)絡發(fā)出請求或者應答網(wǎng)絡請求,使主機間或者一臺計算機上的進程間可以通訊。
socket()函數(shù)
Python 中,我們用 socket()函數(shù)來創(chuàng)建套接字,語法格式如下:
socket.socket([family[, type[, proto]]])
參數(shù)
- family: 套接字家族可以使AF_UNIX或者AF_INET
- AF_INET 使用IPv4協(xié)議
- AF_INET6 使用IPv6協(xié)議
- type: 套接字類型可以根據(jù)是面向連接的還是非連接分為
SOCK_STREAM或SOCK_DGRAM- SOCK_STREAM (TCP連接)
- SOCK_DGRAM(UDP連接)
- protocol: 一般不填默認為0.
socket() 分別傳遞不同的域AF_INET和AF_UNIX
bind()的地址結構分別為sockaddr_in(制定IP端口)和sockaddr_un(指定路徑名)
2 AF_INET需經(jīng)過多個協(xié)議層的編解碼,消耗系統(tǒng)cpu,并且數(shù)據(jù)傳輸需要經(jīng)過網(wǎng)卡,受到網(wǎng)卡帶寬的限制。AF_UNIX數(shù)據(jù)到達內(nèi)核緩沖區(qū)后,由內(nèi)核根據(jù)指定路徑名找到接收方socket對應的內(nèi)核緩沖區(qū),直接將數(shù)據(jù)拷貝過去,不經(jīng)過協(xié)議層編解碼,節(jié)省系統(tǒng)cpu,并且不經(jīng)過網(wǎng)卡,因此不受網(wǎng)卡帶寬的限制。
3 AF_UNIX的傳輸速率遠遠大于AF_INET
AF_INET不僅可以用作本機的跨進程通信,同樣的可以用于不同機器之間的通信,其就是為了在不同機器之間進行網(wǎng)絡互聯(lián)傳遞數(shù)據(jù)而生。而AF_UNIX則只能用于本機內(nèi)進程之間的通信。
使用場景
AF_UNIX由于其對系統(tǒng)cpu的較少消耗,不受限于網(wǎng)卡帶寬,及高效的傳遞速率,本機通信則首選AF_UNIX域。
不用多說,AF_INET則用于跨機器之間的通信。
二 創(chuàng)建UDP協(xié)議的客戶端和服務端
端口數(shù)值的取值范圍是0~65535
端口數(shù)小于1024的都是為眾所周知的網(wǎng)絡服務所保留的
地址127.0.0.1是本機地址;它始終指向當前的計算機。程序可以使用這個地址來連接運行在同一計算機上的其它程序。
常用函數(shù):
- gethostname()返回運行程序所在的計算機的主機名:
>>> import socket
>>> socket.gethostname()
'lenovo'
- gethostbyname(name) 嘗試將給定的主機名解釋為一個IP地址
>>> socket.gethostbyname('lenovo
'192.168.1.4'
>>> socket.gethostbyname('www.jb51.net')
'222.76.216.16'
(1) 客戶端
實例:
import socket
#建立一個UDP的客戶端
udp = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
data = input("請輸入你要說的話")
udp.sendto(data.encode("utf-8"),("127.0.0.1",8858)) #發(fā)送UDP數(shù)據(jù),將數(shù)據(jù)發(fā)送到套接字,address是形式為(ipaddr,port)的元組,指定遠程地址。返回值是發(fā)送的字節(jié)數(shù)。
(2) 服務端
實例:
import socket
#建立一個UDP的服務端
udpServer = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#127.0.0.1 - 127.255.255.254 都屬于訪問本地 但是都是用 127.0.0.1
udpServer.bind(("127.0.0.1",8858))
while True:
data,addr = udpServer.recvfrom(1024) #接收UDP數(shù)據(jù),與recv()類似,但返回值是(data,address)。其中data是包含接收數(shù)據(jù)的字符串,address是發(fā)送數(shù)據(jù)的套接字地址。
print("消息為:",data.decode("utf-8"),"地址為",addr)
三 創(chuàng)建TCP協(xié)議的客戶端和服務端
(1) 創(chuàng)建TCP客戶端
實例
import socket
tcpClient = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcpClient.connect(("127.0.0.1",8000)) #主動初始化TCP服務器連接,。一般address的格式為元組(hostname,port),如果連接出錯,返回socket.error錯誤。
while True:
data = input("輸入倆天內(nèi)容")
tcpClient.send(data.encode("utf-8")) #發(fā)送TCP數(shù)據(jù),將string中的數(shù)據(jù)發(fā)送到連接的套接字。返回值是要發(fā)送的字節(jié)數(shù)量,該數(shù)量可能小于string的字節(jié)大小。
mydata = tcpClient.recv(1024) #接收TCP數(shù)據(jù),數(shù)據(jù)以字符串形式返回,bufsize指定要接收的最大數(shù)據(jù)量。flag提供有關消息的其他信息,通??梢院雎?。
print(mydata.decode("utf-8"))
(2) 創(chuàng)建TCP服務端
實例:
import socket
tcpServer = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcpServer.bind(("127.0.0.1",8000)) #綁定地址(host,port)到套接字, 在AF_INET下,以元組(host,port)的形式表示地址。
tcpServer.listen(5) #設置監(jiān)聽數(shù) ,最大連接數(shù),超過后排隊
clientSock,addr = tcpServer.accept() #被動接受TCP客戶端連接,(阻塞式)等待連接的到來
while True:
data = clientSock.recv(1024) #接收TCP數(shù)據(jù)最大值為1字節(jié)
print("時間為", time.strftime("%Y-%m-%d %H:%M:%S"))
print("收到了",data.decode("utf-8"))
mydata = input("聊天內(nèi)容")
clientSock.send(mydata.encode("utf-8"))
四 使用socket請求網(wǎng)址
import socket
# 創(chuàng)建一個socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連接:
s.connect(('www.xialigang.com', 80))
s.send(b'GET / HTTP/1.1\r\nHost: www.xialigang.com\r\nConnection: close\r\n\r\n')
buffer = []
while True:
# 每次最多接收1k字節(jié):
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer) #將列表的數(shù)據(jù)進行連接
#把HTTP頭和網(wǎng)頁分離一下,把HTTP頭打印出來,網(wǎng)頁內(nèi)容保存到文件:
header, html = data.split(b'\r\n\r\n', 1)
print(header.decode('utf-8'))
print(html)
五 Socket 對象(內(nèi)建)方法
| 函數(shù) | 描述 |
|---|---|
| 服務器端套接字 | |
| s.bind() | 綁定地址(host,port)到套接字, 在AF_INET下,以元組(host,port)的形式表示地址。 |
| s.listen() | 開始TCP監(jiān)聽。backlog指定在拒絕連接之前,操作系統(tǒng)可以掛起的最大連接數(shù)量。該值至少為1,大部分應用程序設為5就可以了。 |
| s.accept() | 被動接受TCP客戶端連接,(阻塞式)等待連接的到來 |
| 客戶端套接字 | |
| s.connect() | 主動初始化TCP服務器連接,。一般address的格式為元組(hostname,port),如果連接出錯,返回socket.error錯誤。 |
| s.connect_ex() | connect()函數(shù)的擴展版本,出錯時返回出錯碼,而不是拋出異常 |
| 公共用途的套接字函數(shù) | |
| s.recv() | 接收TCP數(shù)據(jù),數(shù)據(jù)以字符串形式返回,bufsize指定要接收的最大數(shù)據(jù)量。flag提供有關消息的其他信息,通常可以忽略。 |
| s.send() | 發(fā)送TCP數(shù)據(jù),將string中的數(shù)據(jù)發(fā)送到連接的套接字。返回值是要發(fā)送的字節(jié)數(shù)量,該數(shù)量可能小于string的字節(jié)大小。 |
| s.sendall() | 完整發(fā)送TCP數(shù)據(jù),完整發(fā)送TCP數(shù)據(jù)。將string中的數(shù)據(jù)發(fā)送到連接的套接字,但在返回之前會嘗試發(fā)送所有數(shù)據(jù)。成功返回None,失敗則拋出異常。 |
| s.recvfrom() | 接收UDP數(shù)據(jù),與recv()類似,但返回值是(data,address)。其中data是包含接收數(shù)據(jù)的字符串,address是發(fā)送數(shù)據(jù)的套接字地址。 |
| s.sendto() | 發(fā)送UDP數(shù)據(jù),將數(shù)據(jù)發(fā)送到套接字,address是形式為(ipaddr,port)的元組,指定遠程地址。返回值是發(fā)送的字節(jié)數(shù)。 |
| s.close() | 關閉套接字 |
| s.getpeername() | 返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)。 |
| s.getsockname() | 返回套接字自己的地址。通常是一個元組(ipaddr,port) |
| s.setsockopt(level,optname,value) | 設置給定套接字選項的值。 |
| s.getsockopt(level,optname[.buflen]) | 返回套接字選項的值。 |
| s.settimeout(timeout) | 設置套接字操作的超時期,timeout是一個浮點數(shù),單位是秒。值為None表示沒有超時期。一般,超時期應該在剛創(chuàng)建套接字時設置,因為它們可能用于連接的操作(如connect()) |
| s.gettimeout() | 返回當前超時期的值,單位是秒,如果沒有設置超時期,則返回None。 |
| s.fileno() | 返回套接字的文件描述符。 |
| s.setblocking(flag) | 如果flag為0,則將套接字設為非阻塞模式,否則將套接字設為阻塞模式(默認值)。非阻塞模式下,如果調(diào)用recv()沒有發(fā)現(xiàn)任何數(shù)據(jù),或send()調(diào)用無法立即發(fā)送數(shù)據(jù),那么將引起socket.error異常。 |
| s.makefile() | 創(chuàng)建一個與該套接字相關連的文件 |