socket套接字編程
套接字介紹
1. 套接字 : 實現網絡編程進行數據傳輸的一種技術手段
2. Python實現套接字編程:import socket
3. 套接字分類
>流式套接字(SOCK_STREAM): 以字節流方式傳輸數據,實現tcp網絡傳輸方案。(面向鏈接--tcp協議--可靠的--流式套接字)
>數據報套接字(SOCK_DGRAM):以數據報形式傳輸數據,實現udp網絡傳輸方案。(無鏈接--udp協議--不可靠--數據報套接字)
html
tcp套接字編程
服務端流程
python
1 from socket import * 2 3 #建立流式套接字 4 sockfd = socket(AF_INET,SOCK_STREAM,0) 5 6 #綁定IP端口 7 sockfd.bind(('127.0.0.1',8888)) 8 9 #設置監聽套接字,建立監聽隊列 10 sockfd.listen(5) 11 12 while True: 13 print("waiting for connect....") 14 #等待客戶端連接 15 connfd,addr = sockfd.accept() 16 print("connect from",addr) 17 18 while True: 19 #收發消息 20 21 data = connfd.recv(1024) 22 23 if not data: 24 break 25 26 print(data.decode()) 27 28 #發消息 29 connfd.send('來,確認下眼神'.encode()) 30 #關閉套接字 31 connfd.close() 32 33 sockfd.close()
1. 建立套接字
sockfd=socket.socket(socket_family=AF_INET,socket_type=SOCK_STREAM,proto=0)
功能:建立套接字
參數: socket_family 網絡地址類型 AF_INET表示ipv4
socket_type 套接字類型 SOCK_STREAM(流式) SOCK_DGRAM(數據報)
proto 一般爲0 選擇子協議
返回值: 套接字對象
2. 綁定地址
>本地地址 : 'localhost' , '127.0.0.1'
>網絡地址 : '172.40.91.185'
>自動獲取地址: '0.0.0.0'
sockfd.bind(addr)
功能: 綁定本機網絡地址
參數: 二元元組 (ip,port) ('0.0.0.0',8888)
```
3. 設置監聽
sockfd.listen(n)
功能 : 將套接字設置爲監聽套接字,肯定監聽隊列大小
參數 : 監聽隊列大小
```
4. 等待處理客戶端鏈接請求
connfd,addr = sockfd.accept()
功能: 阻塞等待處理客戶端請求
返回值: connfd 客戶端鏈接套接字
addr 鏈接的客戶端地址
```
5. 消息收發
data = connfd.recv(buffersize)
功能 : 接受客戶端消息
參數 :每次最多接收消息的大小
返回值: 接收到的內容
n = connfd.send(data)
功能 : 發送消息
參數 :要發送的內容 bytes格式
返回值: 發送的字節數
```
6. 關閉套接字
sockfd.close()
功能:關閉套接字
```
客戶端流程 編程
1 # 接受的客戶端 2 3 4 from socket import * 5 6 #建立套接字 7 sockfd = socket(AF_INET,SOCK_STREAM) 8 9 #發起鏈接 10 sockfd.connect(('127.0.0.1',8888)) 11 12 while True: 13 msg = input("發消息>> ") 14 #發送消息 15 sockfd.send(msg.encode()) 16 if not msg: 17 break 18 19 #接收消息 20 data = sockfd.recv(1024) 21 print(data.decode()) 22 23 # msg = input("Msg>>") 24 # scorfd.send(msg.encode()) 25 26 # data = sockfd.recv(1024) 27 # print(data.decode()) 28 29 #關閉 30 sockfd.close()
1. 建立套接字
>注意:只有相同類型的套接字才能進行通訊
2. 請求鏈接 瀏覽器
sockfd.connect(server_addr)
功能:鏈接服務器
參數:元組 服務器地址
3. 收發消息
>注意: 防止兩端都阻塞,recv send要配合
4. 關閉套接字
tcp 套接字數據傳輸特色
>* tcp鏈接中當一端退出,另外一端若是阻塞在recv,此時recv會當即返回一個空字串。
>* tcp鏈接中若是一端已經不存在,仍然試圖經過send發送則會產生BrokenPipeError
>* 一個監聽套接字能夠同時鏈接多個客戶端,也可以重複被鏈接
網絡收發緩衝區
1. 網絡緩衝區有效的協調了消息的收發速度
2. send和recv實際是向緩衝區發送接收消息,當緩衝區不爲空recv就不會阻塞。
tcp粘包
socket.time.sleep(0.1)
>緣由:tcp以字節流方式傳輸,沒有消息邊界。屢次發送的消息被一次接收,此時就會造成粘包。
>影響:若是每次發送內容是一個獨立的含義,須要接收端獨立解析此時粘包會有影響。
>處理方法
>>1. 人爲的添加消息邊界
>>2. 控制發送速度
UDP套接字編程
服務端流程
服務器
1 from socket import * 2 import sys 3 from time import ctime 4 5 6 ADDR = ('127.0.0.1',8888) 7 BUFFERSIZE = 1024 8 9 #建立數據報套接字 10 sockfd = socket(AF_INET,SOCK_DGRAM) 11 #綁定地址 12 sockfd.bind(ADDR) 13 14 #收發消息 15 while True: 16 data,addr = sockfd.recvfrom(BUFFERSIZE) 17 print('recv from %s:%s'%(addr,data.decode())) 18 sockfd.sendto\ 19 (("[%s] 接收到消息"%ctime()).encode(),addr) 20 21 sockfd.close() 22 23 24 25 26 #服務端, udp 27 from socket import * 28 29 HOST = '127.0.0.1' 30 PORT = 9999 31 ADDR = (HOST, PORT) 32 #建立數據報套接字 33 sockfd = socket(AF_INET,SOCK_DGRAM) 34 #綁定地址 35 sockfd.bind(ADDR) 36 #收發消息 37 while True: 38 data,addr = sockfd.recvfrom(1024) 39 print('Receive from %s:%s' % (addr,data.decode())) 40 # sockfd.sendto(("[%s] 接收到消息"%ctime()).encode(),addr) 41 sockfd.sendto("收到消息".encode(), addr) 42 sockfd.close()
1. 建立數據報套接字
sockfd = socket(AF_INET,SOCK_DGRAM)
2. 綁定地址
sockfd.bind(addr)
3. 消息收發
data,addr = sockfd.recvfrom(buffersize)
功能: 接收UDP消息
參數: 每次最多接收多少字節
返回值: data 接收到的內容
addr 消息發送方地址
n = sockfd.sendto(data,addr)
功能: 發送UDP消息
參數: data 發送的內容 bytes格式
addr 目標地址
返回值:發送的字節數
4. 關閉套接字
sockfd.close() 網絡
客戶端流程
app
1 from socket import * 2 import sys 3 4 #從命令行傳入IP和端口 5 #python3 udp_server.py 172.60.50.42 8888 6 if len(sys.argv) < 3: 7 print(''' 8 argv is error!!! 9 input as 10 python3 udp_server.py 172.60.50.42 4260 11 ''') 12 13 HOST = sys.argv[1] 14 PORT = int(sys.argv[2]) 15 ADDR = (HOST,PORT) 16 BUFFERSIZE = 1024 17 18 #建立數據報套接字 19 sockfd = socket(AF_INET,SOCK_DGRAM) 20 21 while True: 22 data = input("消息>>") 23 #直接回車退出 24 if not data: 25 break 26 sockfd.sendto(data.encode(),ADDR) 27 data,addr = sockfd.recvfrom(BUFFERSIZE) 28 print('從服務器收到:',data.decode()) 29 sockfd.close() 30 31 32 #客戶端 33 from socket import * 34 import sys 35 36 #命令行輸入服務器地址 37 if len(sys.argv) < 3: 38 print('''argv is error !! 39 start as 40 python3 udp_client.py 127.0.0.1 8888 41 ''') 42 # raise 43 44 HOST = sys.argv[1] 45 PORT = int(sys.argv[2])#字符串轉int類型 46 ADDR = (HOST,PORT) 47 48 #建立數據報套接字 49 sockfd = socket(AF_INET, SOCK_DGRAM) 50 51 while True: 52 data = input("消息: ") 53 if not data: 54 break 55 sockfd.sendto(data.encode(),ADDR) 56 data,addr = sockfd.recvfrom(1024) 57 print("從服務端收到: ", data.decode()) 58 59 sockfd.close() 60 61 # $ python3 udp_client.py 127.0.0.1 8888 62 #要加ip名和端口名才能運行
1. 建立套接字
2. 收發消息
3. 關閉套接字
---------------
>總結 :tcp套接字和udp套接字編程區別
>>1. 流式套接字是以字節流方式傳輸數據,數據報套接字以數據報形式傳輸
>>2. tcp套接字會有粘包,udp套接字有消息邊界不會粘包
>>3. tcp套接字保證消息的完整性,udp套接字則不能
>>4. tcp套接字依賴listen accept創建鏈接才能收發消息,udp套接字則不須要
>>5. tcp套接字使用send,recv收發消息,udp套接字使用sendto,recvfrom
--------------------- socket
socket套接字屬性
【1】 sockfd.type 套接字類型
【2】 sockfd.family 套接字地址類型
【3】 sockfd.getsockname() 獲取套接字綁定地址
【4】 sockfd.fileno() 獲取套接字的文件描述符
【5】 sockfd.getpeername() 獲取鏈接套接字客戶端地址
【6】 sockfd.setsockopt(level,option,value)
功能:設置套接字選項
參數: level 選項類別 SOL_SOCKET
option 具體選項內容
value 選項值
【7】 sockfd.getsockopt(level,option)
功能 : 獲取套接字選項值
tcp
UDP套接字廣播 ide
1 #廣播接收端 與udp的 2 3 from socket import * 4 5 HOST = '' 6 PORT = 8880 7 8 #建立數據報套接字 9 s = socket(AF_INET,SOCK_DGRAM)#加默認參數0變成流式套接字SOCK_STREAM 10 #固定接收端的端口號 11 s.bind((HOST,PORT)) 12 #設置套接字能夠接收廣播, 有此後變成與udp不一樣 13 s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)#broadcast 14 #設置套接字保護端口號 15 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#reuseaddr 16 17 18 # while True: 19 # try: 20 # message,addr = s.recvfrom(1024) 21 # print("從{}獲取信息{}:".\ 22 # format(addr,message.decode()))#decode將「字節流」按照某種規則轉換成'文本' str變unicode 23 # s.sendto(b"I am here",addr) 24 # except (KeyboardInterrupt,SyntaxError):#從終端ctrl+c的錯 25 # raise 26 # except Exception as e: 27 # print(e) 28 29 # s.close() 30 31 while True: 32 try: 33 message,addr = s.recvfrom(1024) 34 print("獲取信息: {}".\ 35 format(message.decode()))#decode將「字節流」按照某種規則轉換成'文本' str變unicode 36 s.sendto(b"I am here",addr) 37 except (KeyboardInterrupt,SyntaxError):#從終端ctrl+c的錯 38 raise#raise 引起一個異常 39 except Exception as e:#異常類 40 print(e) 41 42 s.close()
1 # 發送端 2 3 from socket import * 4 from time import sleep 5 6 #發送廣播的地址 7 #<broadcast> 8 dest = ('176.122.14.255',8880)#端口號9999一致 9 #建立數據報套接字 10 s = socket(AF_INET,SOCK_DGRAM)#dgram 11 #設置可以發送廣播 12 s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)#broadcast 13 14 while True: 15 sleep(2) 16 s.sendto("什麼鬼啊aaaaaa啦啦啦啦啦啦啦".encode(),dest)#目標地址 17 data,addr = s.recvfrom(1024) 18 print("Received from %s:%s"%(addr,data.decode())) 19 s.close() 20 21 # while True: 22 # data = input('input: ') 23 # s.sendto(("@鴻182:"+ data).encode(),dest) 24 # s.close() 25 # 局域網聊天室,同端口號
* 廣播定義 : 一端發送多點接收
* 廣播地址 : 每一個網絡的最大地址爲發送廣播的地址,向該地址發送,則網段內全部主機都能接收。
TCP套接字之HTTP傳輸
HTTP協議 (超文本傳輸協議)
1. 用途 : 網頁獲取,數據的傳輸
2. 特色
應用層協議,傳輸層使用tcp傳輸
簡單,靈活,不少語言都有HTTP專門接口
無狀態,協議不記錄傳輸內容
http1.1 支持持久鏈接,豐富了請求類型
3. 網頁請求過程
1.客戶端(瀏覽器)經過tcp傳輸,發送http請求給服務端
2.服務端接收到http請求後進行解析
3.服務端處理請求內容,組織響應內容
4.服務端將響應內容以http響應格式發送給瀏覽器
5.瀏覽器接收到響應內容,解析展現
HTTP請求(request)
1 """ 2 httpserver v2.0 3 env: python3.6 4 io多路複用 和 http訓練 5 """ 6 7 from socket import * 8 from select import * 9 10 11 # 具體功能實現 12 class HTTPServer: 13 def __init__(self, host='0.0.0.0', port=8000, dir=None): 14 self.host = host 15 self.port = port 16 self.dir = dir 17 self.address = (host, port) 18 # 多路複用列表 19 self.rlist = [] 20 self.wlist = [] 21 self.xlist = [] 22 # 實例化對象時直接建立套接字 23 self.create_socket() 24 self.bind() 25 26 # 建立套接字 27 def create_socket(self): 28 self.sockfd = socket() 29 self.sockfd.setsockopt(SOL_SOCKET, 30 SO_REUSEADDR, 1) 31 32 # 綁定地址 33 def bind(self): 34 self.sockfd.bind(self.address) 35 36 # 啓動服務 37 def serve_forever(self): 38 self.sockfd.listen(3) 39 print("Listen the port %d" % self.port) 40 # IO多路複用接收客戶端請求 41 self.rlist.append(self.sockfd) 42 while True: 43 rs, wx, xs = select(self.rlist, 44 self.wlist, 45 self.xlist) 46 for r in rs: 47 if r is self.sockfd: 48 c, addr = r.accept() 49 self.rlist.append(c) 50 else: 51 # 處理請求 52 self.handle(r) 53 54 def handle(self, connfd): 55 # 接收HTTP請求 56 request = connfd.recv(4096) 57 # 客戶端斷開 58 if not request: 59 self.rlist.remove(connfd) 60 connfd.close() 61 return 62 # 提取請求內容 (字節串按行分割) 63 request_line = request.splitlines()[0] 64 info = request_line.decode().split(' ')[1] 65 print(connfd.getpeername(), ':', info) 66 67 # 根據請求內容進行數據整理 68 # 分爲兩類 1.請求網頁 2.其餘 69 if info == '/' or info[-5:] == '.html': 70 self.get_html(connfd, info) 71 else: 72 self.get_data(connfd, info) 73 74 # 返回網頁 75 def get_html(self, connfd, info): 76 if info == '/': 77 # 請求主頁 78 filename = self.dir + "/index.html" 79 else: 80 filename = self.dir + info 81 try: 82 fd = open(filename) 83 except Exception: 84 # 網頁不存在 85 response = "HTTP/1.1 404 Not Found\r\n" 86 response += 'Content-Type:text/html\r\n' 87 response += '\r\n' 88 response += '<h1>Sorry....</h1>' 89 else: 90 # 網頁存在 91 response = "HTTP/1.1 200 OK\r\n" 92 response += 'Content-Type:text/html\r\n' 93 response += '\r\n' 94 response += fd.read() 95 finally: 96 # 將響應發送給瀏覽器 97 connfd.send(response.encode()) 98 99 # 其餘數據 100 def get_data(self, connfd, info): 101 response = "HTTP/1.1 200 OK\r\n" 102 response += 'Content-Type:text/html\r\n' 103 response += '\r\n' 104 response += "<h1>Waiting for httpserver 3.0</h1>" 105 connfd.send(response.encode()) 106 107 108 # 用戶使用HTTPServer 109 if __name__ == "__main__": 110 """ 111 經過 HTTPServer類快速搭建服務,展現本身的網頁 112 """ 113 # 用戶決定的參數 114 HOST = '0.0.0.0' 115 PORT = 8000 116 DIR = './static' # 網頁存儲位置 117 118 httpd = HTTPServer(HOST, PORT, DIR) # 實例化對象 119 httpd.serve_forever() # 啓動服務
1 #靜態網頁處理器 2 #採用循環的模式,沒法知足客戶端長鏈接 3 4 from socket import *#網絡鏈接端點,導入模塊 5 6 #處理客戶端請求, 交互 7 def handleClient(connfd):#請求,調用函數 8 request = connfd.recv(2048)#請求套接字接受文件 9 # print('**************') 10 # print(request) 11 # print('**************') 12 #按照行切割請求, 方便查看 13 request_lines = request.splitlines() 14 for line in request_lines: 15 print(line.decode())#對分割後的文件解碼 16 17 try: 18 f = open("index.html") 19 except IOError:#找不到頁面, 文件出錯 20 response = "HTTP/1.1 303 not Found\r\n"#http響應行 21 response += '\r\n'#無響應頭, 有空行 22 response += '''====sorry ,file not find 23 ******************************* 24 Sorry bing i love you miss you. 25 ******************************* 26 ''' 27 else:#正常狀況運行 28 response = "HTTP/1.1 200 OK\r\n"#成功響應頭, 響應體是頁面 29 response += '\r\n' 30 response += f.read()#響應體是頁面 31 # for i in f: 32 # response += i 33 finally: 34 connfd.send(response.encode())#發送給瀏覽器 35 connfd.close() 36 37 #流程控制 38 def main(): 39 sockfd = socket()#建立流式套接字 40 sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#設置端口重用 41 sockfd.bind(('0.0.0.0',8888))#綁定套接字 42 sockfd.listen(10)#監聽套接字 43 while True:#循環監聽等待鏈接 44 print("Listen the port 8000..") 45 connfd,addr = sockfd.accept()#建立另外一個套接字鏈接請求,便於接收一個後再接收, 46 #處理瀏覽器發來的請求 47 handleClient(connfd)#跟客戶頓交互 48 connfd.close()#關閉套接字 49 50 51 if __name__ == "__main__":#調用主函數 52 main() 53 54 55 # --------------------------------------------------------------- 56 57 58 # #接收請求 59 # #查看請求 60 # #返回客戶端請求 61 62 # def handleClient(): 63 # pass 64 65 66 # #建立tcp套接字, 調用handleClient完成功能 67 # def main(): 68 # pass 69 70 # if __name__=="__main__": 71 # main()
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>歡迎進入Alan主頁</title> 6 </head> 7 <body> 8 <h1>Alan songzihong welcome to you </h1> 9 <h1>what is you name</h1> 10 <h1>How can i do cna help you </h1> 11 </body> 12 </html> 13 14 <!-- 沒保存會只顯示文件名 --> 15 <!-- html:5 + tab --> 16 <!-- h1 + tab -->
* 請求行 : 具體的請求類別和請求內容
```
GET / HTTP/1.1
請求類別 請求內容 協議版本
```
請求類別:每一個請求類別表示要作不一樣的事情
```
GET : 獲取網絡資源
POST :提交必定的信息,獲得反饋
HEAD : 只獲取網絡資源的響應頭
PUT : 更新服務器資源
DELETE : 刪除服務器資源
CONNECT
TRACE : 測試
OPTIONS : 獲取服務器性能信息
```
* 請求頭:對請求的進一步解釋和描述
```
Accept-Encoding: gzip
```
* 空行
* 請求體: 請求參數或者提交內容
http響應(response)
1. 響應格式:響應行,響應頭,空行,響應體
* 響應行 : 反饋基本的響應狀況
```
HTTP/1.1 200 OK
版本信息 響應碼 附加信息
```
響應碼 :
```
1xx 提示信息,表示請求被接收
2xx 響應成功
3xx 響應須要進一步操做,重定向
4xx 客戶端錯誤
5xx 服務器錯誤
```
* 響應頭:對響應內容的描述
```
Content-Type: text/html
```
* 響應體:響應的主體內容信息