socket一般也稱做"套接字",用於描述IP地址和端口,是一個通訊鏈的句柄,應用程序一般經過"套接字"向網絡發出請求或者應答網絡請求。web
socket起源於Unix,而Unix/Linux基本哲學之一就是「一切皆文件」,對於文件用【打開】【讀寫】【關閉】模式來操做。 socket就是該模式的一個實現,socket是一種特殊的文件,一些socket函數就是對其進行的操做(打開、讀/寫IO、關閉)。django
socket和file的區別:flask
文件是都是在同一臺計算機上,兩個進程之間傳輸數據。小程序
socket能夠實如今不一樣的計算機之間傳輸數據,也就是網絡傳輸數據。好比說qq、打開一個網頁,這些都是socket來實現通訊的。sublime-text
那網絡通訊呢又要說到tcp/ip協議和udp協議,socket裏面已經封裝好了upd和tcp/ip協議,直接使用就能夠了。安全
簡單說下tcp/ip協議是幹嗎的,網絡剛出來的時候,一片混亂,那要傳輸數據就得你們都遵照一個規則,你們都按照這個,而後就出現了tcp/ip協議。也許你聽過3次握手,4次斷開,說的就是tcp/ip鏈接的一個過程。加入a計算機要和b計算機通訊,過程是這樣的服務器
a:在嗎,我能夠連你嗎
b:在,你連吧
a:好的,我要給你發數據了網絡
#這就是3次握手,這就創建好通道了,兩臺計算機就能夠進行通行了。多線程
那麼4次斷開是什麼呢框架
a:我要和你斷開了
b:好的,你斷開吧
b:關閉通道
a:關閉通道
爲何關閉2次呢,由於兩端要互相傳數據,挖了兩條路,一條路用來b給a傳數據,另一條是a給b傳數據,因此是2次關閉,各自關閉各自的通道。這兩條路呢,就有個次叫全雙工,就是兩邊均可以互相發送數據,若是隻有一端能夠發送數據,那就叫單工。
下面這個圖,就能夠看到創建鏈接的過程和傳輸數據的過程,以及斷開的過程。
而udp協議就比較簡單了,沒有那麼複雜的斷開和鏈接,不須要3次握手,不須要肯定客戶端、服務端是否能收到,tcp/ip是必須創建好鏈接以後,才能發數據,而udp是無鏈接的,知道ip和端口號直接就是發,它比tcp/ip快,可是不安全。
upd就像寫信同樣,有可能在路上就沒有了,對方沒有收到。而tcp/ip就像打電話同樣,必須得接通才能說話。
下面是udp server端的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import socket
'''
使用UDP協議時,不須要創建鏈接,只須要知道對方的IP地址和端口號,就能夠直接發數據包。可是,能不能到達就不知道了。
雖然用UDP傳輸數據不可靠,但它的優勢是和TCP比,速度快,對於不要求可靠到達的數據,就可使用UDP協議。
咱們來看看如何經過UDP協議傳輸數據。和TCP相似,使用UDP的通訊雙方也分爲客戶端和服務器。服務器首先須要綁定端口
綁定端口和TCP同樣,可是不須要調用listen()方法,而是直接接收來自任何客戶端的數據
'''
# ipv4 SOCK_DGRAM指定了這個Socket的類型是UDP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 綁定 客戶端口和地址:
s.bind(('127.0.0.1', 9999))#綁定9999端口號
print('開始聊天了')
while True:
# 接收數據 自動阻塞 等待客戶端請求:
data, addr = s.recvfrom(1024) #接收客戶端發過來的數據和ip地址
data = data.decode()
print('客戶端的ip信息',addr)
print('發過來的數據 %s'%data)
msg = input('你的回覆:') #這個是我們返回的數據
s.sendto(msg.encode(), addr)#把數據發送給客戶端
# recvfrom()方法返回數據和客戶端的地址與端口,這樣,服務器收到數據後,直接調用sendto()就能夠把數據用UDP發給客戶端。
|
下面是client端的代碼
1
2
3
4
5
6
7
8
9
10
11
12
|
import socket
'''
客戶端使用UDP時,首先仍然建立基於UDP的Socket,而後不須要鏈接,直接經過sendto()給服務器發數據:
'''
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
data = input('data:')
s.sendto(data.encode(), ('127.0.0.1', 9999))#
# 發送數據:
recv = s.recv(1024) #返回的數據
print(recv.decode())
# 接收數據:
s.close()
|
先運行server端的代碼再運行client的向server端發送數據,server端再返回數據,作一個簡單的聊天的小程序,結果以下
下面是tcp/ip協議的代碼,server端代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import socket
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #創建一個tcp/ip scoket
sock.bind(('127.0.0.1',9999)) #綁定端口號
sock.listen(128)#監聽,同時能連多少個客戶端
while True:
print('開始等待下一個客戶端過來。。。')
client,addr = sock.accept() #接收到客戶端的socket,和地址
print('接收到 client數據',addr)
while True:
#
data = client.recv(1024)#獲取到客戶端的數據
data = data.decode()
if not data or data=='bye':
#若是沒有發送過來數據就表明客戶端close了,或者發過來bye表明鏈接要斷開
print('服務結束',addr)
client.close()#斷開鏈接,爲下一個服務
break
else:#若是他還在發送的話
print('發過來的', data)
msg = input('回覆:')
client.send(msg.encode()) # 數據
sock.close()
|
#下面是客戶端鏈接服務端的代碼
1
2
3
4
5
6
7
8
9
10
11
12
|
import socket
s = socket.socket()
s.connect(('127.0.0.1',9999)) #鏈接服務端
while True:#
data = input('data:')
s.send(data.encode())#發送數據
recv = s.recv(1024).decode()
print(recv)
if data=='close':
break
s.close()
|
你們可能會想,學這個有啥用呢,其實這些web框架底層就是這麼實現的,好比說django、flask這些,會了socket,咱們也能夠本身開發一個web框架。固然如今只能一次給一個客戶端服務,用了多線程或者多進程就能夠爲多個客戶端來服務了。
服務端運行結果
下面用多線程,每次有客戶端連過來就啓動一個線程來服務,這樣就能夠爲多個客戶端服務了,用threading模塊啓動一個線程,來一個請求就啓動一個線程爲他服務,代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
import socket,threading
class SocketServer:
def __init__(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 創建一個tcp/ip scoket
sock.bind(('127.0.0.1', 9999)) # 綁定端口號
sock.listen(128) # 監聽
self.sock = sock
def start_server(self):
while True:
print('開始等待個客戶端過來')
client,addr = self.sock.accept()
print('客戶【%s】過來了',addr)
t = threading.Thread(target=self.client_recv,args=(client,addr))
t.start()
def client_recv(self,client,addr):
while True:
data = client.recv(1024) # 獲取到客戶端的數據
data = data.decode()
if not data or data == 'bye':
# 若是沒有發送過來數據就表明客戶端close了,或者發過來bye表明鏈接要斷開
print('服務結束', addr)
client.close() # 斷開鏈接,爲下一個服務
break
else: # 若是他還在發送的話
print('發過來的', data)
msg = '統一回復,人不在'
client.send(msg.encode()) # 數據
if __name__ == '__main__':
t = SocketServer()
t.start_server()
|