前情回顾
1. OSI七层模型 tcp/ip模型
2. 三次握手和四次挥手 3. tcp和udp的区别 4. 网络概念 : 网络主机 端口 IP地址 域名 5. 套接字编程 : 网络编程技术手段 流式套接字 : TCP 数据报套接字:UDP6. TCP套接字流程
服务端:socket() --> bind() --> listen() --> accept() --> recv(),send() --> close()客户端:socket() --> connect() --> send(),recv() --> close()
*********************************************************
循环
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import socket 2 3 #创建套接字 4 sockfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 5 #绑定地址 6 sockfd.bind(('0.0.0.0',8888)) 7 8 #设置监听 9 sockfd.listen(5) 10 #等待处理客户连接 11 print("waitting for connect...") 12 connfd,addr = sockfd.accept() 13 print("Connect from",addr)#客户地址 14 15 #收发消息 16 while True: 17 #收 18 data = connfd.recv(1024) 19 if not data: 20 break 21 print("Receive message",data.decode()) 22 #发 23 n= connfd.send(b"Receve your message!!") 24 print("Send %d bytes"%n) 25 #关闭套接字 26 connfd.close()#连接套接字 27 sockfd.close()#监听套接字
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 3 #创套接字 4 sockfd = socket() 5 6 #发起连接 7 server_addr = ('192.168.43.165',8888) 8 sockfd.connect(server_addr) 9 10 #收发消息 11 while True: 12 #发 13 data = input(">>") 14 sockfd.send(data.encode()) 15 if not data: 16 break 17 #收 18 data = sockfd.recv(1024) 19 print("From server:",data.decode()) 20 21 #关闭套接子 22 sockfd.close()
一. tcp 套接字数据传输特点
* tcp连接中当一端退出,另一端如果阻塞在recv,此时recv会立即返回一个空字串。
* tcp连接中如果一端已经不存在,让然试图通过send发送则会产生BrokenPipeError
* 一个监听套接字可以同时连接多个客户端,也能够重复被连接
前:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import socket 2 3 #创建套接字 4 sockfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 5 #绑定地址 6 sockfd.bind(('0.0.0.0',8888)) 7 8 #设置监听 9 sockfd.listen(5) 10 #等待处理客户连接 11 while True: 12 print("waitting for connect...") 13 try: 14 connfd,addr = sockfd.accept() 15 except KeyboardInterrupt: 16 print("Server exit") 17 break 18 print("Connect from",addr)#客户地址 19 #收发消息 20 while True: 21 #收 22 data = connfd.recv(1024) 23 if not data: 24 break 25 print("Receive message",data.decode()) 26 #发 27 n= connfd.send(b"Receve your message!!") 28 print("Send %d bytes"%n) 29 #关闭套接字 30 connfd.close()#连接套接字 31 sockfd.close()#监听套接字
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 3 #创套接字 4 sockfd = socket() 5 6 #发起连接 7 server_addr = ('172.40.71.149',8888) 8 sockfd.connect(server_addr) 9 10 #收发消息 11 while True: 12 #发 13 data = input(">>") 14 sockfd.send(data.encode()) 15 if not data: 16 break 17 #收 18 data = sockfd.recv(1024) 19 print("From server:",data.decode()) 20 21 #关闭套接子 22 sockfd.close() 23
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import socket 2 3 #创建套接字 4 sockfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 5 #绑定地址 6 sockfd.bind(('0.0.0.0',8888)) 7 8 #设置监听 9 sockfd.listen(5) 10 #等待处理客户连接 11 while True: 12 print("waitting for connect...") 13 try: 14 connfd,addr = sockfd.accept() 15 except KeyboardInterrupt: 16 print("Server exit") 17 break 18 print("Connect from",addr)#客户地址 19 #收发消息 20 while True: 21 #收 22 data = connfd.recv(5) 23 if not data: 24 break 25 print("Receive message",data.decode()) 26 #发 27 n= connfd.send(b"Receve your message!!") 28 print("Send %d bytes"%n) 29 #关闭套接字 30 connfd.close()#连接套接字 31 sockfd.close()#监听套接字
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 3 #创套接字 4 sockfd = socket() 5 6 #发起连接 7 server_addr = ('172.40.71.149',8888) 8 sockfd.connect(server_addr) 9 10 #收发消息 11 while True: 12 #发 13 data = input(">>") 14 sockfd.send(data.encode()) 15 if not data: 16 break 17 #收 18 data = sockfd.recv(1024) 19 print("From server:",data.decode()) 20 21 #关闭套接子 22 sockfd.close() 23
【2】 影响:如果每次发送内容是一个独立的含义,需要接收端独立解析此时粘包会有影响。
【3】 处理:1. 人为的添加消息边界
2. 控制发送速度 二. UDP套接字编程1. 服务端流程
【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()2. 客户端流程
【1】 创建套接字 【2】 收发消息 【3】 关闭套接字![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 3 #创建数据报套接字 4 sockfd = socket(AF_INET,SOCK_DGRAM) 5 #绑定地址 6 server_addr=('0.0.0.0',8888) 7 sockfd.bind(server_addr) 8 9 #消息收发 10 while True: 11 try: 12 data,addr = sockfd.recvfrom(5) 13 except KeyboardInterrupt: 14 print("Server exit") 15 break 16 print("Receive from %s:%s"%(addr,data.decode()))#addr地址,返回元组 17 sockfd.sendto(b"Thank for you msg",addr) 18 19 #关闭套接字 20 sockfd.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 3 #定义服务器地址 4 HOST = '172.40.71.149' 5 POST = 8888 6 ADDR = (HOST,POST) 7 8 #创建udp套接字 9 sockfd = socket(AF_INET,SOCK_DGRAM) 10 11 #收发消息 12 while True: 13 data = input("Msg>>") 14 if not data: 15 break 16 sockfd.sendto(data.encode(),ADDR) 17 msg,addr = sockfd.recvfrom(1024) 18 print("Receive from server:",msg.decode()) 19 20 sockfd.close() 21
1. 流式套接字是以字节流方式传输数据,数据报套接字以数据报形式传输
2. tcp套接字会有粘包,udp套接字有消息边界不会粘包 3. tcp套接字保证消息的完整性,udp套接字则不能 4. tcp套接字依赖listen accept建立连接才能收发消息,udp套接字则不需要 5. tcp套接字使用send,recv收发消息,udp套接字使用sendto,recvfrom 二. socket模块方法和socket套接字属性1. 部分socket模块方法 【1】 gethostname() 获取计算机名 【2】 gethostbyname('www.baidu.com') 获取主机ip地址 【3】 getservbyname('mysql') 获取服务端口号 【4】 getservbyport(3306) 获取端口对应服务 【5】 inet_aton('192.168.1.2') 将IP转换为bytes子串 【6】 inet_ntoa(b'\xc0\xa8\x01\x02') 将bytes子串转换为IP地址
2. 套接字属性
【1】 sockfd.type 套接字类型
【2】 sockfd.family 套接字地址类型
【3】 sockfd.getsockname() 获取套接字绑定地址
【4】 sockfd.fileno() 获取套接字的文件描述符 文件描述符:系统中每一个IO操作都会分配一个整数作为编号,该整数即这个IO操作的文件描述符。
特点: 文件描述符是系统用来区分处理IO的标志,不会重复。
【5】 sockfd.getpeername() 获取连接套接字客户端地址
【6】 sockfd.setsockopt(level,option,value) 功能:设置套接字选项 参数: level 选项类别 SOL_SOCKET option 具体选项内容 value 选项值 【7】 sockfd.getsockopt(level,option) 功能 : 获取套接字选项值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 3 #创建套接字对象 4 s = socket() 5 6 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#对套接字设置为可以立即重用端口(绑定前) 7 print(s.getsockopt(SOL_SOCKET,SO_REUSEADDR)) 8 9 print(s.family)#地址类型 10 print(s.type)#套接字类型 11 12 13 s.bind(('172.40.71.149',8888))#先绑定 14 print(s.getsockname())#获取绑定的addr 15 16 print(s.fileno())#获取文件描述符 17 18 #print(s.getpeername()) 19 #OSError: [Errno 107] Transport endpoint is not connected 20 s.listen(3) 21 c,addr = s.accept() 22 print(c.getpeername())#获取对应的客户地址,相当于addr 23
广播定义 : 一端发送多点接收
广播地址 : 每个网络的最大地址为发送广播的地址,向该地址发送,则网段内所有主机都能接收。![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 #创建数据报套接字 3 s = socket(AF_INET,SOCK_DGRAM) 4 #设置可以发送接受广播 5 s.setsockopt(SOL_SOCKET,SO_BROADCAST,1) 6 #选择一个接受地址 7 s.bind(('0.0.0.0',6475)) 8 9 while True: 10 try: 11 msg,addr = s.recvfrom(1024) 12 print("从%s接受广播:%s"%(addr,msg.decode())) 13 except KeyboardInterrupt: 14 break 15 except Exception as e: 16 print(e) 17 s.close() 18
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 from time import sleep 3 4 #目标地址 5 dest =('172.40.71.255',6475) 6 s = socket(AF_INET,SOCK_DGRAM) 7 s.setsockopt(SOL_SOCKET,SO_BROADCAST,1) 8 9 10 data = '''****** 11 枷 12 ******''' 13 14 while True: 15 sleep(2) 16 s.sendto(data.encode(),dest) 17 18 s.close() 19
四. TCP套接字之HTTP传输
1. HTTP协议 (超文本传输协议)
【1】 用途 : 网页获取,数据的传输
【2】 特点 : * 应用层协议,传输层使用tcp传输
* 简单,灵活,很多语言都有HTTP专门接口 * 无状态,协议不记录传输内容 * http1.1 支持持久连接,丰富了请求类型【3】 网页请求过程
1.客户端(浏览器)通过tcp传输,发送http请求给服务端
2.服务端接收到http请求后进行解析 3.服务端处理请求内容,组织响应内容 4.服务端将响应内容以http响应格式发送给浏览器 5.浏览器接收到响应内容,解析展示 【4】 HTTP请求* 请求行 : 具体的请求类别和请求内容 GET / HTTP/1.1
请求类别 请求内容 协议版本请求类别:每个请求类别表示要做不同的事情
GET : 获取网络资源 POST :提交一定的信息,得到反馈 HEAD : 只获取网络资源的响应头 PUT : 更新服务器资源 DELETE : 删除服务器资源 CONNECT TRACE : 测试 OPTIONS : 获取服务器性能信息 * 请求头:对请求的进一步解释和描述Accept-Encoding: gzip
* 空行 * 请求体: 请求参数或者提交内容![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 3 #创建套接字 4 s = socket() 5 s.bind(('0.0.0.0',8000)) 6 s.listen(3) 7 8 c,addr = s.accept()#接收浏览器连接 9 print("Connect from",addr) 10 data = c.recv(4096)#浏览器发送请求 11 print(data) 12 #组织http相应 13 date = '''Http/1.1 200 ok 14 Content-Type:text/html 15 16 hello world 17 ''' 18 c.send(data.encode())#符合http响应格式 19 20 c.close() 21 s.close()
请求类型
3. 能够自己写出tcp udp的基础代码![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 3 s = socket() 4 s.bind(('172.40.71.149',8888)) 5 s.listen(3) 6 7 c,addr = s.accept() 8 print("Connect from",addr) 9 10 11 f= open('mo.jpg','wb') 12 13 while True: 14 data = c.recv(1024) 15 if not data: 16 break 17 f.write(data) 18 19 f.close() 20 c.close() 21 s.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from socket import * 2 3 s = socket() 4 s.connect(('172.40.71.149',8888)) 5 6 f =open('one_list_app.jpg','rb') 7 8 while True: 9 data = f.read(1024) 10 if not data: 11 break 12 s.send(data) 13 14 f.close() 15 s.close()