套接字


套接字

Python 提供了兩個級別訪問的網絡服務。:

  • 低級別的網絡服務支持基本的 Socket,它提供了標准的 BSD Sockets API,可以訪問底層操作系統Socket接口的全部方法。
  • 高級別的網絡服務模塊 SocketServer, 它提供了服務器中心類,可以簡化網絡服務器的開發。

Socket又稱"套接字",應用程序通常通過"套接字"向網絡發出請求或者應答網絡請求,使主機間或者一台計算機上的進程間可以通訊。
Python 中,我們用 socket()函數來創建套接字,語法格式如下:
socket.socket([family[, type[, proto]]])
參數:

  • family: 套接字家族可以使AF_UNIX或者AF_INETAF_INET指定使用IPv4協議,如果要用更先進的IPv6,就指定為AF_INET6
  • type: 套接字類型可以根據是面向連接的還是非連接分為SOCK_STREAMSOCK_DGRAMSOCK_STREAM指定使用面向流的TCP協議
  • protocol: 一般不填默認為0.

Socket 對象(內建)方法

服務器端套接字

函數 描述
s.bind() 綁定地址(host,port)到套接字, 在AF_INET下,以元組(host,port)的形式表示地址。
s.listen() 開始TCP監聽。backlog指定在拒絕連接之前,操作系統可以掛起的最大連接數量。該值至少為1,大部分應用程序設為5就可以了。
s.accept() 被動接受TCP客戶端連接,(阻塞式)等待連接的到來

客戶端套接字

函數 描述
s.connect() 主動初始化TCP服務器連接,。一般address的格式為元組(hostname,port),(port指端口號)如果連接出錯,返回socket.error錯誤。
s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常

公共用途的套接字函數

函數 描述
s.recv() 接收TCP數據,數據以字符串形式返回,bufsize指定要接收的最大數據量。flag提供有關消息的其他信息,通常可以忽略。
s.send() 發送TCP數據,將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。
s.sendall() 完整發送TCP數據,完整發送TCP數據。將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常。
s.recvform() 接收UDP數據,與recv()類似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。
s.sendto() 發送UDP數據,將數據發送到套接字,address是形式為(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。
s.close() 關閉套接字
s.getpeername() 返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)
s.getsockname() 返回套接字自己的地址。通常是一個元組(ipaddr,port)
s.setsockopt(level,optname,value) 設置給定套接字選項的值。
s.getsockopt(level,optname[.buflen]) 返回套接字選項的值。
s.settimeout(timeout) 設置套接字操作的超時期,timeout是一個浮點數,單位是秒。值為None表示沒有超時期。一般,超時期應該在剛創建套接字時設置,因為它們可能用於連接的操作(如connect()
s.gettimeout() 返回當前超時期的值,單位是秒,如果沒有設置超時期,則返回None
s.fileno() 返回套接字的文件描述符。
s.setblocking(flag) 如果flag為0,則將套接字設為非阻塞模式,否則將套接字設為阻塞模式(默認值)。非阻塞模式下,如果調用recv()沒有發現任何數據,或send()調用無法立即發送數據,那么將引起socket.error異常。
s.makefile() 創建一個與該套接字相關連的文件
# 導入socket庫:
import socket

# 創建一個socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連接:
s.connect(('www.sina.com.cn', 80))

客戶端

客戶端要主動發起TCP連接,必須知道服務器的IP地址端口號。新浪網站的IP地址可以用域名www.sina.com.cn自動轉換到IP地址

80端口是Web服務的標准端口。其他服務都有對應的標准端口號,例如SMTP服務是25端口,FTP服務是21端口,等等。端口號小於1024的是Internet標准服務的端口,端口號大於1024的,可以任意使用。

因此,我們連接新浪服務器的代碼如下:
s.connect(('www.sina.com.cn', 80))
建立TCP連接后,我們就可以向新浪服務器發送請求,要求返回首頁的內容:

# 發送數據:
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
60
# 接收數據:
buffer = []
while True:
    # 每次最多接收1k字節:
    d = s.recv(1024)
    if d:
        buffer.append(d)
    else:
        break
data = b''.join(buffer)
# 關閉連接:
s.close()

接收數據時,調用recv(max)方法,一次最多接收指定的字節數,因此,在一個while循環中反復接收,直到recv()返回空數據,表示接收完畢,退出循環。
當我們接收完數據后,調用close()方法關閉Socket,這樣,一次完整的網絡通信就結束了。
接收到的數據包括HTTP頭和網頁本身,我們只需要把HTTP頭和網頁分離一下,把HTTP頭打印出來,網頁內容保存到文件:

(header, html) = data.split(b'\r\n\r\n', 1)
print(header.decode('utf-8'))
# 把接收的數據寫入文件:
with open('sina.html', 'wb') as f:
    f.write(html)
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 22 Aug 2017 08:03:04 GMT
Content-Type: text/html
Content-Length: 601703
Connection: close
Last-Modified: Tue, 22 Aug 2017 07:58:35 GMT
Vary: Accept-Encoding
Expires: Tue, 22 Aug 2017 08:04:03 GMT
Cache-Control: max-age=60
X-Powered-By: shci_v1.03
Age: 0
Via: http/1.1 cnc.beixian.ha2ts4.205 (ApacheTrafficServer/4.2.1.1 [cMsSf ]), http/1.1 ctc.ningbo.ha2ts4.106 (ApacheTrafficServer/4.2.1.1 [cRs f ])
X-Cache: MISS.205
X-Via-CDN: f=edge,s=ctc.ningbo.ha2ts4.101.nb.sinaedge.com,c=218.75.27.189;f=Edge,s=ctc.ningbo.ha2ts4.106,c=115.238.190.101;f=edge,s=cnc.beixian.ha2ts4.213.nb.sinaedge.com,c=115.238.190.106;f=Edge,s=cnc.beixian.ha2ts4.205,c=115.238.190.101
X-Cache: MISS.MERGE.106

現在,只需要在瀏覽器中打開這個sina.html文件,就可以看到新浪的首頁了。

服務器:

  • 我們使用 socket 模塊的 socket() 函數來創建一個 socket對象。socket 對象可以通過調用其他函數來設置一個 socket 服務。
    • 通過調用 bind(hostname, port) 函數來指定服務的 port(端口)。
    • 我們調用 socket 對象的 accept 方法。該方法等待客戶端的連接,並返回 connection 對象,表示已連接到客戶端。

完整代碼如下

# 文件名:server.py
import threading
# 導入 socket模塊
import socket

# 創建 socket 對象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 


# 綁定端口
s.bind(('127.0.0.1', 1024))

# 設置最大連接數,超過后排隊
s.listen(5)
print('請稍后,正在連接中...')

# 每個連接都必須創建新線程(或進程)來處理,否則,單線程在處理連接的過程中,無法接受其他客戶端的連接:
def tcplink(sock, addr):
    print('Accept new connection from %s:%s...' % addr)
    sock.send(b'Welcome!')
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if not data or data.decode('utf-8') == 'exit':
            break
        sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
    sock.close()
    print('Connection from %s:%s closed.' % addr)
    
while True:
    # 建立客戶端連接
    sock,addr = serversocket.accept()
    # 創建新線程來處理TCP連接:
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()
請稍后,正在連接中...
  • 客戶端
    接下來我們寫一個簡單的客戶端實例連接到以上創建的服務。端口號為 9999。
    socket.connect(hosname, port ) 方法打開一個 TCP 連接到主機為 hostname 端口為 port 的服務商。連接后我們就可以從服務端后期數據,記住,操作完成后需要關閉連接。

完整代碼如下:

# 文件名:client.py

# 導入 socket模塊
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立連接:
s.connect(('127.0.0.1', 9999))
# 接收歡迎消息:
print(s.recv(1024).decode('utf-8'))
for data in [b'Michael', b'Tracy', b'Sarah']:
    # 發送數據:
    s.send(data)
    print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()

現在我們打開兩個終端,第一個終端執行 server.py 文件
第二個終端執行 client.py 文件

Python Internet 模塊

以下列出了 Python 網絡編程的一些重要模塊:

協議 功能用處 端口號 Python 模塊
HTTP 網頁訪問 80 httplib, urllib, xmlrpclib
NNTP 閱讀和張貼新聞文章,俗稱為"帖子" 119 nntplib
FTP 文件傳輸 20 ftplib, urllib
SMTP 發送郵件 25 smtplib
POP3 接收郵件 110 poplib
IMAP4 獲取郵件 143 imaplib
Telnet 命令行 23 telnetlib
Gopher 信息查找 70 gopherlib, urllib


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM