您好,登錄后才能下訂單哦!
早期的計算機網絡,都是由廠商規定自己的通信協議,互不兼容,為了把全世界不同類型的計算機連接起來,就必須規定一套全球通用的協議,所以就出現了TCP/IP
要解決怎么標識一個進制,在一臺電腦上可以同pid標識進程,但是在網絡上是做不到的,其實TCP/IP就幫我們解決了這個問題,網絡層的IP可以標識在網絡上的主機,而傳輸層的協議+端口就可以標識主機中
socket是進程通信的的一種方式,它與其他進程通信的不同是,它能實現不同主機之間的進程通信,我們網絡的應用大多數都是采用這種方式進行通信的
在Python中使用socket模塊
import socket
socket.socket(AddressFamily, Type)
函數socket可以創建一個socket對象,該函數存在兩個參數
Address Family:可以選擇 AF_INET(用于 Internet 進程間通信) 或者 AF_UNIX(用于同一臺機器進程間通信),實際工作中常用AF_INET
Type:套接字類型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 協議)或者 SOCK_DGRAM(數據報套接字,主要用于 UDP 協議)
創建一個tcp套接字
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.close()
創建一個udp套接字
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.close()
Socket函數
bind(address) 將套接字綁定到地址,在AF_INET下,以元祖(hsot,port)的形式表示地址
listen(backlog) 開始監聽TCP傳入連接,backlog指定可以掛起的最大連接數
accept() 接收TCP連接并返回(conn,address),其中conn是新的套接字對象,address是連接客戶端的地址
connect(address) 連接到address處的套接字,以元祖(hsot,port)的形式表示地址,連接出錯返回socket.error錯誤
connect_ex(address) 功能與s.connect(address) ,但是成功返回0,失敗返回errno的值
recv(bufsize[,flag]) 接收TCP套接字的數據,數據以字節形式返回,bufsize指定接收的最大數據量,flag提供有關消息的其他信息,通常可以忽略
send(string[,flag]) 發送TCP數據,將string中的數據發送到連接的套接字,返回值是要發送的字節數量
sendall(string[],flag) 完整的發送TCP數據,返回之前會嘗試發送所有數據,成功返回Nonne,失敗拋出異常
recvfrom(bufsize[,flag]) 接收UDP套接字的數據,與s.recv()類似,但返回值是(data,address),data表示接收的數據,address表示發送數據的套接字地址
sendto(string[,flag],address) 發送UDP數據,將數據發送到套接字,address是形式為(ipaddr,port)的元組,返回值是發送的字節數
close() 關閉套接字
getpeername() 返回連接套接字的遠程地址,返回值是形式為(ipaddr,port)的元組
getsockname() 返回u套接字自己的地址,返回值是形式為(ipaddr,port)的元組
setsockopt(level,optname,value) 設置給定套接字選項的值
setsockopt(level,optname[.buflen]) 返回套接字選項的值
settimeout(timeout) 設置套接字及操作的朝時期,tiemout為一個浮點數,單位是秒,值為None表示永遠沒有朝時期
setblocking(flag) 如果flag為0,則將套接字設為非阻塞模式,非阻塞模式下,如果調用recv()沒有接收到任何數據,或send()無法發送數據,將引起socket.error異常
客戶端
from socket import *
# 創建socket
tcp_client_socket = socket(AF_INET, SOCK_STREAM)
# 目的信息
server_ip = input("請輸入服務器ip:")
server_port = int(input("請輸入服務器port:"))
# 鏈接服務器
tcp_client_socket.connect((server_ip, server_port))
# 提示用戶輸入數據
send_data = input("請輸入要發送的數據:")
tcp_client_socket.send(send_data.encode("gbk"))
# 接收對方發送過來的數據,最大接收1024個字節
recvData = tcp_client_socket.recv(1024)
print('接收到的數據為:', recvData.decode('gbk'))
# 關閉套接字
tcp_client_socket.close()
服務端
from socket import *
# 創建socket
tcp_server_socket = socket(AF_INET, SOCK_STREAM)
# 綁定
tcp_server_socket.bind(('',9420))
# 使用socket創建的套接字默認的屬性是主動的,使用listen將其變為被動的,這樣就可以接收別人的鏈接了
tcp_server_socket.listen(128)
# 等待連接,產生一個新的socket
client_socket, clientAddr = tcp_server_socket.accept()
# 接收對方發送過來的數據
recv_data = client_socket.recv(1024) # 接收1024個字節
print('接收到的數據為:', recv_data.decode('gbk'))
# 發送一些數據到客戶端
client_socket.send("thank you !".encode('gbk'))
# 關閉套接字,只要關閉了,就意味著為不能再為這個客戶端服務了,如果還需要服務,只能再次重新連接
client_socket.close()
tcp_server_socket.close()
客戶端
from socket import *
def main():
tcp_client_socket = socket(AF_INET, SOCK_STREAM)
server_ip = input("請輸入服務器ip:")
server_port = int(input("請輸入服務器port:"))
tcp_client_socket.connect((server_ip, server_port))
file_name = input("請輸入要下載的文件名:")
tcp_client_socket.send(file_name.encode("utf-8"))
msg = ''
while True:
recv_data = tcp_client_socket.recv(1024)
msg += recv_data.decode('utf-8')
if len(recv_data) < 1024:
break
if msg:
with open(file_name + 'bak', "w") as f:
f.write(msg)
tcp_client_socket.close()
if __name__ == "__main__":
main()
服務端
from socket import *
import sys
def get_file_content(file_name):
"""獲取文件的內容"""
try:
with open(file_name, "rb") as f:
content = f.read()
return content
except:
print("沒有下載的文件:%s" % file_name)
def main():
tcp_server_socket = socket(AF_INET, SOCK_STREAM)
tcp_server_socket.bind(('',9420))
tcp_server_socket.listen(128)
while True:
client_socket, clientAddr = tcp_server_socket.accept()
recv_data = client_socket.recv(1024)
file_name = recv_data.decode("utf-8")
print("對方請求下載的文件名為:%s" % file_name)
file_content = get_file_content(file_name)
if file_content:
client_socket.send(file_content)
client_socket.close()
tcp_server_socket.close()
if __name__ == "__main__":
main()
client向server發起連接
server接收到請求,雙方建立連接
client向server發送消息
server回應client
一次讀寫完畢,連接繼續
直到client發起關閉請求
client向server發起連接
server接收到請求,雙方建立連接
client向server發送消息
server回應client
一次讀寫完成,client發起斷開連接請求
長連接
短連接
長連接可以省去較多的TCP創建和關閉的操作,減少浪費,節約時間,對于頻繁請求資源的場景來說,適合用長連接,但是隨著客戶端連接越來越多,server端早晚扛不住,這時候就需要采取一些策略,例如關閉一些長時間沒有讀取的連接,這樣可以避免惡意連接,還可以限制每個客戶端的最長連接數,這樣可以避免某個客戶端拖后腿,短連接控制簡單,不需要控制手機,但是如果客戶頻繁的請求資源,那就比較操蛋了,浪費時間,浪費帶寬
長連接適用于操作頻繁,點對點的的通訊,而且連接數不是太多的情況,每個TCP需要三次握手,如果每個操作都是先連接,再操作,會浪費很長的時間,所以每個操作之后我們就不給它斷開,再次操作直接發送請求就可以了,例如,數據庫
像WEB網站的http服務一般采用短連接,因為長連接對服務器占用的資源太多,而且http服務的連接數一般不會太少,服務器難說能扛得住,所以并發量高的場景,最好采用短連接
from socket import *
udp_socket = socket(AF_INET, SOCK_DGRAM)
dest_addr = ('', 9420)
send_data = input("請輸入要發送的數據:")
udp_socket.sendto(send_data.encode('utf-8'), dest_addr)
recv_data = udp_socket.recvfrom(1024)
print(recv_data[0].decode('gbk'))
print(recv_data[1])
udp_socket.close()
import socket
def send_msg(udp_socket):
msg = input("\n請輸入要發送的數據:")
dest_ip = input("\n請輸入對方的ip地址:")
dest_port = int(input("\n請輸入對方的port:"))
udp_socket.sendto(msg.encode("utf-8"), (dest_ip, dest_port))
def recv_msg(udp_socket):
recv_msg = udp_socket.recvfrom(1024)
recv_ip = recv_msg[1]
recv_msg = recv_msg[0].decode("utf-8")
print(">>>%s:%s" % (str(recv_ip), recv_msg))
def main():
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.bind(("", 9420))
while True:
print("="*30)
print("1:發送消息")
print("2:接收消息")
print("="*30)
op_num = input("請輸入要操作的功能序號:")
if op_num == "1":
send_msg(udp_socket)
elif op_num == "2":
recv_msg(udp_socket)
else:
print("輸入有誤,請重新輸入...")
if __name__ == "__main__":
main()
面向連接,通信雙方必須建立連接才能進行數據的傳輸,雙方必須為對象分配必要的系統資源,TCP發送的每個報文段都必須得到接收方的應答才認為傳輸成功,發送端如果在規定時間內沒有收到接收端的應答,發送端會將報文段重新發送,TCP還會進行數據校驗,還會通過流量控制機制避免主機發送太快而讓接收端接收不到數據,完成數據交換后,通信雙方必須斷開連接,以釋放系統資源,這種連接是點對點的,因此TCP不適用廣播應用程序
?UDP并不提供對IP協議的可靠機制、流控制以及錯誤恢復功能等,由于UDP比較簡單, UDP頭包含很少的字節,比 TCP 負載消耗少,UDP 適用于不需要 TCP 可靠機制的情形,QQ就是采用的UDP協議
TCP
UDP
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。