一、認識套接字
TCP用主機的IP地址加上主機上的端口號作為TCP連接的端點,這種端點就叫做套接字(socket)或插口。
套接字用(IP地址:端口號)表示。
它是網絡通信過程中端點的抽象表示,包含進行網絡通信必需的五種信息:連接使用的協議,本地主機的IP地址,本地進程的協議端口,遠地主機的IP地址,遠地進程的協議端口。
套接字(socket)是一種通信機制,憑借這種機制,客戶/服務器系統的開發工作既可以在本地單機上進行,也可以跨網絡進行,Linux所提供的功能(如打印服 務,ftp等)通常都是通過套接字來進行通信的,套接字的創建和使用與管道是有區別的,因為套接字明確地將客戶和服務器區分出來,套接字可以實現將多個客 戶連接到一個服務器。
但是說了半天,還是理解不了什么是套接字,其實這是一個狗屎一樣的翻譯問題:
谷歌翻譯中socket直譯為插座,字典中關於socket的解釋為:an electrical device receiving a plug or light bulb to make a connection。譯為:接收插頭或燈泡以進行連接的電氣設備。用c/s模型圖來更好的理解插座的意義:

服務器就像一個大插排,包含很多插座,客戶端就是像一個插頭,每一個線程代表一條電線,客戶端將電線的插頭插到服務器插排上對應的插座上,就可以開始通信了。
數據傳輸需要通過協議,而套接字就是給數據傳輸添加協議的。
二、套接字特性
1、套接字屬性
套接字的特性由3個屬性確定,他們是,域,類型和協議
域指定套接字通信中使用的網絡介質,最常見的套接字域是AF_INET,它指的是Internet網絡
2、套接字類型
一個套接字可能有多種不同的通信方式
流套接字,流套接字提供一個有序,可靠,雙向節流的鏈接,流套接字由類型SOCK_STREAM指定,它是在AF_INET域中通過TCP/IP鏈接實現的,這就是套接字類型(其實就是通信方式)
與流套接字相反,由類型SOCK_DGRAM指定的數據報套接字不建立和維持一個連接,它對可以發送的數據長度有限制,數據報作為一個單獨的網絡消息被傳輸,它可能會丟失,復制或亂序
3、套接字協議
套接字協議,通常使用默認就可以了(也就是最后一個參數填0)
三、基於套接字的TCP網絡編程
1、創建流程概述
- socket創建一個套接字,SOCK_DGRAM是用在UDP上的,而SOCK_STREAM是用於TCP協議的。
- bind綁定IP和port
- listen使套接字變為可以被動鏈接
- accept等待客戶端的鏈接
- close關閉資源
2、socket模塊常用於TCP的方法
- connect(address):連接遠程計算機
- send():發送數據
- recv():接收數據
- listen:開始監聽,等待客戶端連接
客戶端實例:
-
from socket import *
-
#建立套接字,第二個參數是流式套接字 tcp專用
-
clientd_socket = socket(AF_INET,SOCK_STREAM)
-
#目標主機,也就是服務器的ip和端口
-
serverAddress = ( '10.10.16.194',9999)
-
#主動和服務器發起鏈接
-
clientd_socket.connect(serverAddress)
-
-
data = input( '請輸入數據')
-
#發送數據
-
clientd_socket.send(data.encode( 'utf-8'))
-
#接受數據
-
re = clientd_socket.recv( 128)
-
print(re.decode( 'utf-8'))
-
-
clientd_socket.close()
- accept():響應客戶端的請求
阻塞當前線程
如果沒有計算機連接,一直等待
如果有計算機連接,響應客戶端
服務端實例:
-
from socket import *
-
server_socket = socket(AF_INET,SOCK_STREAM)
-
-
local_address = ( '10.10.16.194',9999)
-
#綁定本機ip和端口,那么此程序就只接收此端口好的所有網絡鏈接
-
server_socket.bind(local_address)
-
-
#限制接受5個客戶端的數據
-
server_socket.listen( 5)
-
#接受鏈接,如果沒有鏈接那么處於阻塞狀態。
-
#有鏈接的時候,accept會返回兩個值,第一個是socket鏈接,第二個是遠程地址
-
lianjie,remoteAddress = server_socket.accept()
-
re = lianjie.recv( 128)
-
print(re.decode( 'utf-8'))
-
#發送數據
-
data = input( '請輸入發送內容')
-
lianjie.send(data.encode( 'utf-8'))
-
-
lianjie.close() #server_socket中的其中一個鏈接
-
server_socket.close() #server_socket相當於一個大的容器
先運行服務端,在運行客戶端:結果
客戶端:

服務端:

3、改進一下,讓程序不會在一收一發后停止
客戶端:
-
-
from socket import *
-
clientd_socket = socket(AF_INET,SOCK_STREAM)
-
serverAddress = ( '10.10.16.194',9999)
-
clientd_socket.connect(serverAddress)
-
while True:
-
data = input( '請輸入數據')
-
clientd_socket.send(data.encode( 'utf-8'))
-
re = clientd_socket.recv( 128)
-
print(re.decode( 'utf-8'))
服務端:
-
from socket import *
-
server_socket = socket(AF_INET,SOCK_STREAM)
-
local_address = ( '10.10.16.194',9999)
-
server_socket.bind(local_address)
-
server_socket.listen( 5)
-
lianjie,remoteAddress = server_socket.accept()
-
while True:
-
re = lianjie.recv( 128)
-
print(re.decode( 'utf-8'))
-
data = input( '請輸入發送內容')
-
lianjie.send(data.encode( 'utf-8'))
-
lianjie.close()
-
server_socket.close()
