概要:Socket又稱"套接字",應用程序通常通過"套接字"向網絡發出請求或者應答網絡請求,使主機間或者一臺計算機上的進程間可以通訊。
一、套接字對象
Python 中,我們用 socket()函數來創建套接字,語法格式如下:
socket.socket([family[, type[, proto]]])
- family: 套接字家族可以使AF_UNIX或者AF_INET
- type: 套接字類型可以根據是面向連接的還是非連接分爲SOCK_STREAM或SOCK_DGRAM
- protocol: 默認值.
構造器定義如下:
def __init__(self, family=-1, type=-1, proto=-1, fileno=None):
# For user code address family and type values are IntEnum members, but
# for the underlying _socket.socket they're just integers. The
# constructor of _socket.socket converts the given argument to an
# integer automatically.
if fileno is None:
if family == -1:
family = AF_INET
if type == -1:
type = SOCK_STREAM
if proto == -1:
proto = 0
_socket.socket.__init__(self, family, type, proto, fileno)
self._io_refs = 0
self._closed = False
二、內建函數(參考菜鳥教程)
函數 | 描述 |
---|---|
bind() | 綁定地址(host,port)到套接字, 在AF_INET下,以元組(host,port)的形式表示地址。 |
listen() | 開始TCP監聽。backlog指定在拒絕連接之前,操作系統可以掛起的最大連接數量。該值至少爲1,大部分應用程序設爲5就可以了。 |
accept() | 被動接受TCP客戶端連接,(阻塞式)等待連接的到來 |
connect() | 主動初始化TCP服務器連接,。一般address的格式爲元組(hostname,port),如果連接出錯,返回socket.error錯誤。 |
connect_ex() | connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常 |
recv() | 接收TCP數據,數據以字符串形式返回,bufsize指定要接收的最大數據量。flag提供有關消息的其他信息,通常可以忽略。 |
send() | 發送TCP數據,將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。 |
sendall() | 完整發送TCP數據,完整發送TCP數據。將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常。 |
recvfrom() | 接收UDP數據,與recv()類似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。 |
sendto() | 發送UDP數據,將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。 |
close() | 關閉套接字 |
getpeername() | 返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)。 |
getsockname() | 返回套接字自己的地址。通常是一個元組(ipaddr,port) |
setsockopt(level,optname,value) | 設置給定套接字選項的值。 |
getsockopt(level,optname[.buflen]) | 返回套接字選項的值。 |
settimeout(timeout) | 設置套接字操作的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。一般,超時期應該在剛創建套接字時設置,因爲它們可能用於連接的操作(如connect()) |
gettimeout() | 返回當前超時期的值,單位是秒,如果沒有設置超時期,則返回None。 |
fileno() | 返回套接字的文件描述符。 |
setblocking(flag) | 如果flag爲0,則將套接字設爲非阻塞模式,否則將套接字設爲阻塞模式(默認值)。非阻塞模式下,如果調用recv()沒有發現任何數據,或send()調用無法立即發送數據,那麼將引起socket.error異常。 |
makefile() | 創建一個與該套接字相關連的文件 |
三、通訊示例
- 服務端
#!/usr/bin/python3
import socket
import threading
# 創建 socket 對象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 獲取本地主機名
host = socket.gethostname()
port = 6666
# 綁定IP和端口號
server_socket.bind((host, port))
# 設置最大連接數
server_socket.listen(10)
client_socket, addr = server_socket.accept()
print("客戶端 %s已連接" % (addr,))
def print_msg():
while True:
try:
data = client_socket.recv(1024).decode()
if data == 'exit':
client_socket.close()
else:
print(">>> " + data)
except ConnectionResetError:
print("客戶端 %s已下線!" % (addr,))
break
# 啓動異步線程用於輸出客戶端信息
thread = threading.Thread(name="Server", target=print_msg)
thread.start()
while True:
send_msg = input()
client_socket.send(send_msg.encode('utf-8'))
- 客戶端
#!/usr/bin/python3
import socket
import threading
# 創建 socket 對象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 獲取本地主機名
host = socket.gethostname()
# 設置端口號
port = 6666
# 連接服務,指定主機和端口
client_socket.connect((host, port))
def print_msg():
while True:
try:
msg = client_socket.recv(1024).decode()
print(">>> " + msg)
except Exception:
print("已成功下線!")
break
# 啓動異步線程用於輸出服務端信息
thread = threading.Thread(name="Client", target=print_msg)
thread.start()
send_msg = input()
while send_msg != 'exit':
client_socket.send(send_msg.encode('utf-8'))
send_msg = input()
else:
client_socket.close()
- 結果
服務端
客戶端 ('192.168.0.104', 53131)已連接
>>> 你好,在嗎?
在,怎麼了?
>>> 沒事,再見
客戶端 ('192.168.0.104', 53131)已下線!
客戶端
你好,在嗎?
>>> 在,怎麼了?
沒事,再見
exit
已成功下線!