套接字
首先,我們應先理解什么事套接字。套接字是一種具有之前所說的“通信端點”概念的計算機網絡數據結構。網絡化的應用程序在開始任何通訊之前都必須要創建套接字。就像電話的插口一樣,沒有它就沒辦法通信。
套接字有兩種,分別是基於文件型的和基於網絡型的。
套接字家族包括AF_UNIX,AF_LOCAL,AF_INET和AF_NETLINK。
python只支持AF_UNIX,AF_INET和AF_NETLINK。因我們只關心網絡編程,所以我們只用AF_INET。
SOCKET()模塊
套接字模塊是一個非常簡單的基於對象的接口,它提供對低層BSD套接字樣式網絡的訪問。使用該模塊可以實現客戶機和服務器套接字。要在python 中建立具有TCP和流套接字的簡單服務器,需要使用socket模塊。利用該模塊包含的函數和類定義,可生成通過網絡通信的程序。
socket內建方法
函數 | 描述 |
服務器端套接字函數 | |
s.bind() | 綁定地址(主機,端口號對)到套接字 |
s.listen() | 開始TCP 監聽 |
s.accept() | 被動接受TCP 客戶的連接,(阻塞式)等待連接的到來 |
客戶端套接字函數 | |
s.connect() | 主動初始化TCP 服務器連接 |
s.connect_ex() | connect()函數的擴展版本,出錯時返回出錯碼,而不是拋異常 |
公共用途的套接字函數 | |
s.recv() | 接收TCP 數據 |
s.send() | 發送TCP 數據 |
s.sendall() | 完整發送TCP 數據 |
s.recvfrom() | 接收UDP 數據 |
s.sendto() | 發送UDP 數據 |
s.getpeername() | 連接到當前套接字的遠端的地址 |
s.getsockname() | 當前套接字的地址 |
s.getsockopt() | 返回指定套接字的參數 |
s.setsockopt() | 設置指定套接字的參數 |
s.close() | 關閉套接字 |
面向模塊的套接字函數 | |
s.setblocking() | 設置套接字的阻塞與非阻塞模式 |
s.settimeout()a | 設置阻塞套接字操作的超時時間 |
s.gettimeout()a | 得到阻塞套接字操作的超時時間 |
面向文件的套接字的函數 | |
s.fileno() | 套接字的文件描述符 |
s.makefile() | 創建一個與該套接字關連的文件 |
a. Python 2.3 版本新加入的函數 |
連接方式分TCP和UDP兩種
分別看一下
TCP方式
server端
server端的socket一般流程是這樣:
1. 建立一個socket(可以選擇socket類型INET,UNIX等,以及連接方式TCP/UDP)
socket=socket.socket(familly,type)
family的值可以是AF_UNIX(Unix域,用於同一台機器上的進程間通訊),也可以是AF_INET(對於IPV4協議的TCP和 UDP),至於type參數,SOCK_STREAM(流 套接字)或者 SOCK_DGRAM(數據報文套接字),SOCK_RAW(raw套接字)。
2. 使用bind公開一個端口,使得client可以方便連接
socket.bind(address)
address必須是一個雙元素元組,((host,port)),主機名或者ip地址+端口號。如果端口號正在被使用或者保留,或者主機名或ip地址錯誤,則引發socke.error異常。
3. 設置一個listen隊列的大小
socket.listen(backlog)
backlog指定了最多連接數,至少為1,接到連接請求后,這些請求必須排隊,如果隊列已滿,則拒絕請求。
4. 服務器套接字通過socket的accept方法等待客戶請求一個連接:
connection,address=socket.accept()
調用accept方法時,socket會進入'waiting'(或阻塞)狀態。客戶請求連接時,方法建立連接並返回服務器。accept方法返回 一個含有倆個元素的元組,
形如(connection,address)。第一個元素(connection)是新的socket對象,服務器通過它與客 戶通信;第二個元素(address)是客戶的internet地址。
5. 通過send()/recv()來對socket進行讀寫操作
服務器調用send,並采用字符串形式向客戶發送信息。send方法 返回已發送的字符個數。服務器使用recv方法從客戶接受信息。調用recv時,必須指定一個整數來控制本次調用所接受的最大數據量。recv方法在接受 數據時會進入'blocket'狀態,最后返回一個字符串,用它來表示收到的數據。如果發送的量超過recv所允許,數據會被截斷。多余的數據將緩沖於接 受端。以后調用recv時,多余的數據會從緩沖區刪除。
代碼示例
import socket s=socket.socket() s.bind(('xxx.xxx.xxx.xxx',xxxx)) #ip地址和端口號s.listen(5) cs,address = s.accept() print 'got connected from',address cs.send('byebye') ra=cs.recv(512) print ra cs.close()
client端
- 創建一個socket以連接服務器 socket=socket.socket(family,type)
- 使用socket的connect方法連接服務器 socket.connect((host,port))
- 客戶和服務器通過send和recv方法通信。
- 結束后,客戶通過調用socket的close方法來關閉連接。
代碼示例
import socket s=socket.socket() s.connect(('xxx.xxx.xxx.xxx',xxxx)) #與服務器程序ip地址和端口號相同data=s.recv(512) s.send('hihi') s.close() print 'the data received is',data
測試代碼
服務器端
#!/usr/bin/python # -*- coding: utf-8 -*- import socket from time import ctime ''' host為空表示bind可以綁定到所有有效地址上 port 必須要大於1024 bufsiz為緩沖區 我們設置為1K ''' host = '' port = 23456 bufsiz = 1024 ADDR = (host,port) tcpSerSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) #參數表示允許多少連接同時連進來 try: while True: ''' 進入服務器的無限循環中,等待連接到來 有鏈接時,進入對話循環,等待客戶發送數據,如果消息為空,表示客戶端已經退出,等待下一個客戶端連接 得到客戶端消息后在消息前加一個時間戳后返回 ''' print 'waiting for connection...' tcpSerSock,addr = tcpSerSock.accept() print '...connected from ',addr while True: data = tcpSerSock.recv(bufsiz) if not data: break tcpSerSock.send('[%s] %s' %(ctime(),data)) except BaseException, e: tcpSerSock.close() #記住在服務器退出時記得關閉
客戶端
#!/usr/bin/python # -*- coding: utf-8 -*- import socket host = '127.0.0.1' port = 23456 bufsiz = 1024 ADDR = (host,port) tcpCliSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcpCliSock.connect(ADDR) while True: data = raw_input('> ') if not data: break tcpCliSock.send(data) data = tcpCliSock.recv(bufsiz) if not data: break print data tcpCliSock.close()
UDP方式
UDP號稱無連接傳輸,全然沒有TCP那么復雜,三次握手,錯誤重傳之類的機制都沒有,發的只管發,收得只管收,收到沒有?不知道,順序不對怎么 辦?不管!就是這樣,但是速度就要比TCP高得多了。在對數據幀要求不是很高的地方,這確實是很好用的,比如網絡上的視頻傳輸,音頻傳輸等。
server端
1. 建立數據報形式的socket
2. 公開一個端口,一邊客戶端連接
3. 開始接收數據
def udpServer(): address = ('xxx.xxx.xxx.xxx', xxxx) srvsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) srvsock.bind(address) #data,addr = srvsock.recvfrom(2048) if __name__ == "__main__": udpServer()
server中address元組中的引號表示可以接受任何地址來的數據報,TCP例子中的則表示可以接受任意地址發起的連接。
client端
1. 新建一個數據報socket
2. 收發數據
def udpClient(): address = ('xxx.xxx.xxx.xxx', xxxx) clisock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #clisock.sendto(data, address) if __name__ == "__main__": udpClient()
測試代碼
服務器端
#!/usr/bin/python # -*- coding: utf-8 -*- import socket from time import ctime ''' host為空表示bind可以綁定到所有有效地址上 port 必須要大於1024 bufsiz為緩沖區 我們設置為1K ''' host = '' port = 23456 bufsiz = 1024 ADDR = (host,port) udpSerSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udpSerSock.bind(ADDR) try: while True: print 'waiting for connection...' data,addr = udpSerSock.recvfrom(bufsiz) udpSerSock.sendto('[%s] %s' %(ctime(),data),addr) if data == 'exit': break print '...received from and returned to:',addr except BaseException, e: print e udpSerSock.close()
客戶端
#!/usr/bin/python # -*- coding: utf-8 -*- import socket host = '127.0.0.1' port = 23456 bufsiz = 1024 ADDR = (host,port) udpCliSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) while True: data = raw_input('> ') if not data: break udpCliSock.sendto(data,ADDR) data,ADDR = udpCliSock.recvfrom(bufsiz) if not data: break print data udpCliSock.close()
參考鏈接:http://www.iteye.com/topic/401391
http://www.cppblog.com/lai3d/archive/2008/02/19/42919.html