前情回顾
1. OSI七层模型 tcp/ip模型
2. 三次握手和四次挥手
3. tcp和udp的区别
4. 网络概念 : 网络主机 端口 IP地址 域名
5. 套接字编程 : 网络编程技术手段
流式套接字 : TCP
数据报套接字:UDP
6. TCP套接字流程
服务端:socket() --> bind() --> listen() --> accept() --> recv(),send() --> close()
客户端:socket() --> connect() --> send(),recv() --> close()
*********************************************************
循环
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()#监听套接字
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
* 一个监听套接字可以同时连接多个客户端,也能够重复被连接
前:
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()#监听套接字
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
* 网络收发缓冲区
【1】网络缓冲区有效的协调了消息的收发速度
【2】send和recv实际是向缓冲区发送接收消息,当缓冲区不为空recv就不会阻塞。
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()#监听套接字
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
* tcp粘包
【1】 原因:tcp以字节流方式传输,没有消息边界。多次发送的消息被一次接收,此时就会形成粘包。
【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】 关闭套接字
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()
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
总结 :tcp套接字和udp套接字编程区别
1. 流式套接字是以字节流方式传输数据,数据报套接字以数据报形式传输
2. tcp套接字会有粘包,udp套接字有消息边界不会粘包
3. tcp套接字保证消息的完整性,udp套接字则不能
4. tcp套接字依赖listen accept建立连接才能收发消息,udp套接字则不需要
5. tcp套接字使用send,recv收发消息,udp套接字使用sendto,recvfrom
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'xc0xa8x01x02') 将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)
功能 : 获取套接字选项值
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
三. UDP套接字广播
广播定义 : 一端发送多点接收
广播地址 : 每个网络的最大地址为发送广播的地址,向该地址发送,则网段内所有主机都能接收。
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
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
* 空行
* 请求体: 请求参数或者提交内容
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()
1. 使用tcp完成一个文件的传输,将文件从客户端发送给服务端。要求文件可以是文本,也可以是图片
2. 记住http请求格式和请求行每部分含义。了解
请求类型
3. 能够自己写出tcp udp的基础代码
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()
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()