擊右上方紅色按鈕關注“web秀”,讓你真正秀起來
在很多需求業務中,都需要瀏覽器和服務器實時通信來實現功能,比如:掃碼登錄(掃碼后,手機確認登錄,PC網頁完成登錄并跳轉)、訂單語言提醒等,這些都是建立在兩端實時通信的基礎上的。對前端而言,來實現瀏覽器和服務器實時通信,最好的選擇就是Socket.IO庫,能夠快速的實現兩端實時通信功能。
Socket.IO是一個WebSocket庫,可以在瀏覽器和服務器之間實現實時,雙向和基于事件的通信。它包括:Node.js服務器庫、瀏覽器的Javascript客戶端庫。它會自動根據瀏覽器從WebSocket、AJAX長輪詢、Iframe流等等各種方式中選擇最佳的方式來實現網絡實時應用,非常方便和人性化,而且支持的瀏覽器最低達IE5.5
(1)、支持瀏覽器/Nodejs環境
(2)、支持雙向通信
(3)、API簡單易用
(4)、支持二進制傳輸
(5)、減少傳輸數據量
(1)客戶端
npm install vue-socket.io --save
main.js添加下列代碼
import VueSocketIO from 'vue-socket.io' Vue.use(new VueSocketIO({ debug: true, // 服務器端地址 connection: 'http://localhost:3000', vuex: { } }))
發送消息和監聽消息
//發送信息給服務端 this.$socket.emit('login',{ username: 'username', password: 'password' }); //接收服務端的信息 this.sockets.subscribe('relogin', (data)=> { console.log(data) })
(2)服務端
服務端,我們基于express搭建node服務器。
npm install --save express npm install --save socket.io
index.js文件
var app=require('express')(); var http=require('http').Server(app); var io=require('socket.io')(http); app.get('/', function(req, res){ res.send('<h1>你好web秀</h1>'); }); io.on('connection',function(socket) { //接收數據 socket.on('login', function (obj) { console.log(obj.username); // 發送數據 socket.emit('relogin', { msg: `你好${obj.username}`, code: 200 }); }); }); http.listen(3000, function(){ console.log('listening on *:3000'); });
然后啟動服務端服務
node index.js
客戶端即可查看效果。
io.on('connect', onConnect); function onConnect(socket){ // 發送給當前客戶端 socket.emit( 'hello', 'can you hear me?', 1, 2, 'abc' ); // 發送給所有客戶端,除了發送者 socket.broadcast.emit( 'broadcast', 'hello friends!' ); // 發送給同在 'game' 房間的所有客戶端,除了發送者 socket.to('game').emit( 'nice game', "let's play a game" ); // 發送給同在 'game1' 或 'game2' 房間的所有客戶端,除了發送者 socket.to('game1').to('game2').emit( 'nice game', "let's play a game (too)" ); // 發送給同在 'game' 房間的所有客戶端,包括發送者 io.in('game').emit( 'big-announcement', 'the game will start soon' ); // 發送給同在 'myNamespace' 命名空間下的所有客戶端,包括發送者 io.of('myNamespace').emit( 'bigger-announcement', 'the tournament will start soon' ); // 發送給指定 socketid 的客戶端(私密消息) socket.to(<socketid>).emit( 'hey', 'I just met you' ); // 包含回執的消息 socket.emit( 'question', 'do you think so?', function (answer) {} ); // 不壓縮,直接發送 socket.compress(false).emit( 'uncompressed', "that's rough" ); // 如果客戶端還不能接收消息,那么消息可能丟失 socket.volatile.emit( 'maybe', 'do you really need it?' ); // 發送給當前 node 實例下的所有客戶端(在使用多個 node 實例的情況下) io.local.emit( 'hi', 'my lovely babies' ); };
喜歡小編或者覺得小編文章對你有幫助的,可以點擊一波關注哦!
戶端/服務端架構
OSI七層
為何學習socket一定要先學習互聯網協議:
socket層
socket層是什么
套接字發展史及分類
基于文件類型的套接字家族
基于網絡類型的套接字家族
套接字工作流程
通信實例
服務端
客戶端
結果
基于TCP的套接字
TCP服務端
TCP客戶端
打電話示例
服務端
客戶端
問題解決
解決方法一
解決方法二
socket函數解義
服務端套接字函數
客戶端套接字函數
公共用途的套接字函數
面向鎖的套接字方法
面向文件的套接字的函數
客戶端/服務端架構
1.硬件C/S架構(打印機)
2.軟件C/S架構
互聯網中處處是C/S架構
如黃色網站是服務端,你的瀏覽器是客戶端(B/S架構也是C/S架構的一種)
騰訊作為服務端為你提供視頻,你得下個騰訊視頻客戶端才能看它的視頻)
C/S架構與socket的關系:
我們學習socket就是為了完成C/S架構的開發,我們需要通過socket開發一個服務端和客戶端,來完3成特定的功能。
OSI七層
須知一個完整的計算機系統是由硬件、操作系統、應用軟件三者組成,具備了這三個條件,一臺計算機系統就可以自己跟自己玩了(打個單機游戲,玩個掃雷啥的),如果你要跟別人一起玩,那你就需要上網了,什么是互聯網?互聯網的核心就是由一堆協議組成,協議就是標準,比如全世界人通信的標準是英語
如果把計算機比作人,互聯網協議就是計算機界的英語。所有的計算機都學會了互聯網協議,那所有的計算機都就可以按照統一的標準去收發信息從而完成通信了。人們按照分工不同把互聯網協議從邏輯上劃分了層級。
詳見網絡通信原理:http://www.cnblogs.com/linhaifeng/articles/5937962.html
為何學習socket一定要先學習互聯網協議:
1.首先:本節課程的目標就是教會你如何基于socket編程,來開發一款自己的C/S架構軟件
2.其次:C/S架構的軟件(軟件屬于應用層)是基于網絡進行通信的
3.然后:網絡的核心即一堆協議,協議即標準,你想開發一款基于網絡通信的軟件,就必須遵循這些標準。
4.最后:就讓我們從這些標準開始研究,開啟我們的socket編程之旅
socket層
socket層是什么
Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
所以,我們無需深入理解tcp/udp協議,socket已經為我們封裝好了,我們只需要遵循socket的規定去編程,寫出的程序自然就是遵循tcp/udp標準的。
也有人將socket說成ip+port,ip是用來標識互聯網中的一臺主機的位置,而port是用來標識這臺機器上的一個應用程序,ip地址是配置到網卡上的,而port是應用程序開啟的,ip與port的綁定就標識了互聯網中獨一無二的一個應用程序而程序的pid是同一臺機器上不同進程或者線程的標識。
套接字發展史及分類
套接字起源于 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 Unix,即人們所說的 BSD Unix。 因此,有時人們也把套接字稱為“伯克利套接字”或“BSD 套接字”。一開始,套接字被設計用在同 一臺主機上多個應用程序之間的通訊。這也被稱進程間通訊,或 IPC。套接字有兩種(或者稱為有兩個種族),分別是基于文件型的和基于網絡型的。
基于文件類型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,可以通過訪問同一個文件系統間接完成通信
基于網絡類型的套接字家族
套接字家族的名字:AF_INET
(還有AF_INET6被用于ipv6,還有一些其他的地址家族,不過,他們要么是只用于某個平臺,要么就是已經被廢棄,或者是很少被使用,或者是根本沒有實現,所有地址家族中,AF_INET是使用最廣泛的一個,python支持很多種地址家族,但是由于我們只關心網絡編程,所以大部分時候我么只使用AF_INET
套接字工作流程
一個生活中的場景。你要打電話給一個朋友,先撥號,朋友聽到電話鈴聲后提起電話,這時你和你的朋友就建立起了連接,就可以講話了。等交流結束,掛斷電話結束此次交談。 生活中的場景就解釋了這工作原理。
先從服務器端說起。服務器端先初始化Socket,然后與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。在這時如果有個客戶端初始化一個Socket,然后連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了??蛻舳税l送數據請求,服務器端接收請求并處理請求,然后把回應數據發送給客戶端,客戶端讀取數據,最后關閉連接,一次交互結束
通信實例
服務端
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2018/3/2 15:12
# @Author : CaiChangEn
# @Software: PyCharm
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # SOCK_STREAM:基于TCP協議 AF_INET:網絡socket通信
phone.bind(('127.0.0.1',8080)) # 綁定地址
phone.listen(5) # 最大連接數
conn,addr=phone.accept() # 拿到一個連接和地址
msg=conn.recv(1024) # 接收信息,1024
print('客戶端發來的消息是[%s]' %msg.decode('utf-8'))
conn.send(msg.upper()) # 發送消息
conn.close()
phone.close()
客戶端
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2018/3/2 15:12
# @Author : CaiChangEn
# @Software: PyCharm
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080)) # 連接本地的服務端
phone.send('hello'.encode('utf-8')) # 發送消息
msg=phone.recv(1024) # 收消息
print('服務端發來的消息是[%s]' %msg.decode('utf-8'))
phone.close() # 關閉程序
結果
服務端:客戶端發來的消息是[hello]
客戶端:服務端發來的消息是[HELLO]
基于TCP的套接字
TCP服務端
ss=socket() # 創建服務器套接字
ss.bind() #把地址綁定到套接字
ss.listen() #監聽連接
inf_loop: #服務端無限循環
cs=ss.accept() #接受客戶端連接
comm_loop: #通訊循環
cs.recv()/cs.send() #對話(接收消息/發送消息)
cs.close() #關閉客戶端套接字
ss.close() #關閉服務端套接字(可選)
TCP客戶端
cs=socket() # 創建客戶套接字
cs.connect() # 嘗試連接服務器
comm_loop: # 通訊循環
cs.send()/cs.recv() # 對話(發送/接收)
cs.close() # 關閉客戶套接字
打電話示例
服務端
#_*_coding:utf-8_*_
__author__='Linhaifeng'
import socket
ip_port=('127.0.0.1',8081)#電話卡
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機
s.bind(ip_port) #手機插卡
s.listen(5) #將連接轉為被動連接
while True: #新增接收鏈接循環,可以不停的接電話
conn,addr=s.accept() #手機接電話
# print(conn)
# print(addr)
print('接到來自%s的電話' %addr[0])
while True: #新增通信循環,可以不斷的通信,收發消息
msg=conn.recv(BUFSIZE) #聽消息,聽話
# if len(msg)==0:break #如果不加,那么正在鏈接的客戶端突然斷開,recv便不再阻塞,死循環發生
print(msg,type(msg))
conn.send(msg.upper()) #發消息,說話
conn.close() #掛電話
s.close() #手機關機
服務端改進版
客戶端
#_*_coding:utf-8_*_
__author__='Linhaifeng'
import socket
ip_port=('127.0.0.1',8081)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect_ex(ip_port) #撥電話
while True: #新增通信循環,客戶端可以不斷發收消息
msg=input('>>: ').strip()
if len(msg)==0:continue
s.send(msg.encode('utf-8')) #發消息,說話(只能發送字節類型)
feedback=s.recv(BUFSIZE) #收消息,聽話
print(feedback.decode('utf-8'))
s.close() #掛電話
客戶端改進版
問題解決
有的同學在重啟服務端時可能會遇到下圖這個情況,這個是由于你的服務端仍然存在四次揮手的time_wait狀態在占用地址(如果不懂,請深入研究1.tcp三次握手,四次揮手 2.syn洪水攻擊 3.服務器高并發情況下會有大量的time_wait狀態的優化方法)
解決方法一
#加入一條socket配置,重用ip和端口
phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加,SO_REUSEADDR(重新使用套接字)
phone.bind(('127.0.0.1',8080))
解決方法二
發現系統存在大量TIME_WAIT狀態的連接,通過調整linux內核參數解決,
vi /etc/sysctl.conf
編輯文件,加入以下內容:
net.ipv4.tcp_syncookies=1
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_fin_timeout=30
然后執行 /sbin/sysctl -p 讓參數生效。
net.ipv4.tcp_syncookies=1 表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊,默認為0,表示關閉;
net.ipv4.tcp_tw_reuse=1 表示開啟重用。允許將TIME-WAIT sockets重新用于新的TCP連接,默認為0,表示關閉;
net.ipv4.tcp_tw_recycle=1 表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉。
net.ipv4.tcp_fin_timeout 修改系統默認的 TIMEOUT 時間
socket函數解義
服務端套接字函數
s.bind() 綁定(主機,端口號)到套接字
s.listen() 開始TCP監聽
s.accept() 被動接受TCP客戶的連接,(阻塞式)等待連接的到來
客戶端套接字函數
s.connect() 主動初始化TCP服務器連接
s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
公共用途的套接字函數
s.recv() 接收TCP數據
s.send() 發送TCP數據(send在待發送數據量大于己端緩存區剩余空間時,數據丟失,不會發完)
s.sendall() 發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大于己端緩存區剩余空間時,數據不丟失,循環調用send直到發完)
s.recvfrom() 接收UDP數據
s.sendto() 發送UDP數據
s.getpeername() 連接到當前套接字的遠端的地址
s.getsockname() 當前套接字的地址
s.getsockopt() 返回指定套接字的參數
s.setsockopt() 設置指定套接字的參數
s.close() 關閉套接字
面向鎖的套接字方法
s.setblocking() 設置套接字的阻塞與非阻塞模式
s.settimeout() 設置阻塞套接字操作的超時時間
s.gettimeout() 得到阻塞套接字操作的超時時間
面向文件的套接字的函數
s.fileno() 套接字的文件描述符
s.makefile() 創建一個與該套接字相關的文件
篇文章給大家帶來的內容是關于python中socket網絡編程的詳細介紹(附示例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。
網絡通信的三要素
通信的時候, 雙方必須知道對方的標識, 好比發郵件必須知道對方的郵件地址。 互聯網上每個計算機的唯一標識就是IP地址, 類似 123.123.123.123 。
IP地址實際上是一個32位整數(稱為IPv4) , 以字符串表示的IP地址,如 192.168.0.1 實際上是把32位整數按8位分組后的數字表示, 目的是便于閱讀。
IPv6地址實際上是一個128位整數, 它是目前使用的IPv4的升級版, 以字符串表示。類似于 2001:0db8:85a3:0042:1000:8a2e:0370:7334 。
在兩臺計算機通信時, 只發IP地址是不夠的, 因為同一臺計算機上跑著多個網絡程序。 一個IP包來了之后, 到底是交給瀏覽器還是QQ, 就需要端口號來區分。
每個網絡程序都向操作系統申請唯一的端口號, 這樣, 兩個進程在兩臺計算機之間建立網絡連接就需要各自的IP地址和各自的端口號。
為了把全世界的所有不同類型的計算機都連接起來, 就必須規定一套全球通用的協議,大家把互聯網的協議簡稱TCP/IP協議
socket編程
Socket是網絡編程的一個抽象概念。 通常我們用一個Socket表示“打開了一個網絡鏈接”, 而打開一個Socket需要知道目標計算機的IP地址和端口號, 再指定協議類型即可。
import socket
# socket.gethostname()當不傳入參數時,返回本機主機名
print(socket.gethostname()) #DESKTOP-EN0LQJH
#域名(英語:Domain Name),簡稱域名、網域,是由一串用點分隔的名字組成的Internet上某一臺計算機或計算機組的名稱
#通過域名查找服務器IP地址
print(socket.gethostbyname('www.baidu.com')) #119.75.216.20
#通過一個IPv4的地址來獲取主機信息
print(socket.gethostbyaddr('114.114.114.114'))
#通過IP/port組合,訪問服務器信息
# 如果是AF_INET則格式為:(address, port),如果為AF_INET6,則返回(address, port, flow info, scope id)
print(socket.getaddrinfo('www.xunlei.com',80))
用socket實現簡易的web服務器
import socket
def reply_info(socketObj):
# socketObj.send('ok')
with open('hello.html') as f:
socketObj.send(f.read().encode('utf-8'))
if __name__=="__main__":
#1. 創建socket對象
socket_server=socket.socket()
#2. 綁定IP和端口
socket_server.bind(('192.168.1.165',9999))
print("服務器端已經啟動9999端口......")
#3. 監聽是否有客戶端連接
socket_server.listen(5)
while True:
# 4. 接受客戶端的連接
socket_client,address=socket_server.accept()
# 5. 接收客戶端發來的信息
data=socket_client.recv(1024)
# 6. 與客戶端進行交互, 返回給客戶端信息
reply_info(socket_server)
socket_server.close()
以上就是python中socket網絡編程的詳細介紹(附示例)的詳細內容,更多請關注其它相關文章!
更多技巧請《轉發 + 關注》哦!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。