python进阶之 网络编程
1.tcp和udp协议的区别
TCP协议 面向连接\可靠\慢\对传递的数据的长短没有要求 两台机器之间要想传递信息必须先建立连接 之后在有了连接的基础上,进行信息的传递 可靠 : 数据不会丢失 不会重复被接收 慢 : 每一次发送的数据还要等待结果 三次握手和四次挥手 UDP协议 无连接\不可靠\快\不能传输过长的数据0 机器之间传递信息不需要建立连接 直接发就行 不可靠 : 数据有可能丢失![]()
import socket
sk = socket.socket()
sk.bind(('0.0.0.0',9090))
sk.listen(5)
conn,addr = sk.accept()
accept 相当于和客户端的connect 一起完成了TCP的三次握手
至于之前的sk, 它只起到一个大门的作用了, 意思是说,欢迎敲门, 进门之后我将为你生成一个独一无二的socket描述符!
2.socket模块
什么是socket? 建立网络通信连接至少要一对端口号(socket)。 socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口; HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。 两种套接字:基于文件和面向网络的 基于文件的:AF_UNIX 基于网络的:AF_INIT 特殊意义的解释socket: socekt又称为‘套接字’,用于描述IP和地址端口,是一个通信链路的句柄,应用程序通常通过套接字向网络发出请求或者应答网络请求。 socket起源于Unix,所以也遵从“一切皆文件”的基本哲学,对于文件,进行打开/读取/关闭的操作模式。 socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。 socket和file文件的区别: file模块是针对指定文件进行打开、读写、关闭操作。 socket模块是针对服务器和客户端socket进行打开、读写、关闭操作。
python中socket模块: 地址簇: socket.AF_INET IPv4(默认) socket.AF_INET6 IPv6 socket.AF_UNIX 只能够用于单一的Unix系统进程间通信 类型: socket.SOCK_STREAM 流式socket , for TCP (默认) socket.SOCK_DGRAM 数据报式socket , for UDP
tcp/ip和http的关系? tcp/ip协议是传输层协议,主要解决数据如何在网络中传输,而HTTP协议是应用层协议,主要解决如何包装数据。
我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容。 如果想要使传输的数据有意义,则必须使用到应用层协议。 应用层协议有很多,比如HTTP、FTP、TELNET等,也可以自己定义应用层协议。 WEB使用HTTP协议作应用层协议,以封装HTTP文本信息,然后使用TCP/IP做传输层协议将它发到网络上。”
socket对象
Socket对象 sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) 参数一:地址簇 参数 描述 socket.AF_INET IPv4(默认) socket.AF_INET6 IPv6 ocket.AF_UNIX 只能够用于单一的Unix系统进程间通信 参数二:类型 参数 描述 socket.SOCK_STREAM 流式socket , for TCP (默认) socket.SOCK_DGRAM 数据报式socket , for UDP socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。 socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。 socket.SOCK_SEQPACKET 可靠的连续数据包服务 Socket类方法 方法 描述 s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。 sk.listen(backlog) 开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。 sk.setblocking(bool) 是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。 sk.accept() 接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。 sk.connect(address) 连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 sk.connect_ex(address) 同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061 sk.close() 关闭套接字连接 sk.recv(bufsize[,flag]) 接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。 sk.recvfrom(bufsize[.flag]) 与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 sk.send(string[,flag]) 将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。 sk.sendall(string[,flag]) 将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。内部通过递归调用send,将所有内容发送出去。 sk.sendto(string[,flag],address) 将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。 sk.settimeout(timeout) 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。 sk.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 sk.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port) sk.fileno() 套接字的文件描述符
socket对象
基于tcp协议(流式socket):是两个进程之间的通信,通过端口号来区分不同进程
import socket
sk = socket.socket() #)返回的用于监听和接受客户端的连接请求的套接字
# sk.bind(('192.168.16.96',9090))
sk.bind(('0.0.0.0',9090)) #能接收到所有的ip的访问
sk.listen(5) #监听链接,并且设置监听个数,
conn,addr = sk.accept()
# hold住,等待用户链接,accept()接受一个客户端的连接请求,并返回一个新的套接字,与客户端通信是通过这个新的套接字上发送和接收数据来完成的。
#每个连接进来的客户端,都会通过accept函数返回一个不同的客户端的socket对象和属于客户端的套接字
#bytes:是字节 b'kobe'
#str:字符串类型 'kobe'
#str----编码(encode)---->bytes
#bytes-----解码(decode)--->str
#英文字符串可以直接加b转成bytes
#中文的必须的加上''.encode('utf-8')
#发送回复信息,在网络传输中的最小单位为字节,所以,要将数据转为字节格式
conn.send('我接受到了'.encode('utf-8'))
ret = conn.recv(4096)
print(ret.decode('utf-8'))
conn.close() #conn.close和cilent的sk.close()是四次挥手的过程
sk.close() #关闭socket,不接受任何client请求
import socket
sk =socket.socket()
sk.connect(('192.168.16.96',9090)) #只和server的accept对应
ret = sk.recv(1024)
print(ret.decode('utf-8'))
sk.send('你好啊'.encode('utf-8'))
sk.close()
socket服务端
import socket
sk =socket.socket()
sk.connect(('192.168.16.96',9090)) #只和server的accept对应
ret = sk.recv(1024)
print(ret.decode('utf-8'))
sk.send('你好啊'.encode('utf-8'))
sk.close()
socket客户端
基于udp协议(报文式socket)
import socket
sk = socket.socket(type=socket.SOCK_DGRAM) #SOCK_DGRAM指的是udp协议
sk.bind(('192.168.16.96',8081))
while True:
msg,client_addr = sk.recvfrom(1024)
#在udp协议中,recvfrom接收返回的时候能接收到客户端的信息msg和客户端的链接信息client_addr,
#在tcp协议中,在等待连接的conn,addr = sk.accept()会接受到conn客户端的socket和客户端的链接信息
print(str(client_addr)+":"+msg.decode('utf-8'))
content = input('>>>')
sk.sendto(content.encode('utf-8'),client_addr)
#通过sendto和客户端的连接信息发送消息
sk.close()
socket服务器端
import socket
client_addr= ('192.168.16.96',8081)
sk = socket.socket(type=socket.SOCK_DGRAM)
while True:
connect = input(">>>>")
if connect.upper() !='Q':
sk.sendto(connect.encode('utf-8'),client_addr)
msg = sk.recv(1024).decode('utf-8')
if msg.upper() == 'Q': break
print(str(client_addr)+":"+msg)
else:
break
socket客户端
注意:
1.tcp协议服务端是通过accept来建立链接,获取客户端的链接信息
conn,addr = sk.accept()
通过recv来获取客户端消息
msg = sk.recv(1024).decode('utf-8') 通过send发送信息 conn.send('hello'.encode('utf-8'))
客户端需要连接服务端地址 sk.connect(服务器地址和端口) 客户端发送信息 sk.send('你好'.encode('utf-8')) 客户端接收信息 msg = sk.recv(1024).deocde('1024')
2.udp协议服务器是通过recvfrom来获取客户端发送的信息和客户端链接的信息 msg,addr1 = sk.recvfrom(1024) 通过sendto发送信息给客户端,要指定客户端信息 sk.sendto('hello'.encode('utf-8'),addr1)
udp客户端不需要链接服务器端,是通过sendto发送信息 sk.sendto('你好'.encode('utf-8'),服务器地址和端口) 通过recv来获取信息 msg = sk.recv(1024).decode('utf-8')
3.tcp黏包
什么叫做黏包?
一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据。
为什么只有TCP通讯存在粘包?
主要原因是TCP是以流的方式来处理数据,并且能发送大量的数据,再加上网络上MTU的往往小于在应用处理的消息数据,所以就会引发一次接收的数据无法满足消息的需要,导致粘包的存在。
TCP协议拆包机制
当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。 MTU是Maximum Transmission Unit的缩写。意思是(网卡)网络上传送的最大数据包。MTU的单位是字节。 大部分网络设备的MTU都是1500。 如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令时又会接到之前执行的另外一部分结果,这种就是黏包。
面向流的通信特点和Nagle(优化)算法
TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。 收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。 这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。 对于空消息:tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),也可以被发送,udp协议会帮你封装上消息头发送过去。 可靠黏包的tcp协议:tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
黏包的两种情况
1,发送方的缓存机制:发送端需要等缓冲区满才发送出去,造成黏包(发送数据时间间隔很短,数据很小,会合到一起,产生黏包)
连续send两次且数据很小
2,接收方的缓存机制:接收不及时接收缓冲区的包,造成多个包接收(客户端发送一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿走上次剩余的数据,产生黏包。)
连续recv两次且第一个recv接收的数据小
黏包处理
# -*- coding: utf-8 -*-
# @Time : 2019/4/10 16:58
# @Author : p0st
# @Site :
# @File : 传递大文件server.py
# @Software: PyCharm
import time
import json
import struct
import socket
start_time = time.time()
sk = socket.socket()
ip_addr = (('127.0.0.1',9999))
sk.bind(ip_addr)
sk.listen(5)
conn,addr = sk.accept()
num = conn.recv(4)
l_num = struct.unpack('i',num)[0] #bytes类型的json的长度为45,因为struct.unpack是元组类型的
l_dic = conn.recv(l_num).decode('utf-8') #将bytes类型的json转成字符串类型的json
dic = json.loads(l_dic)
filesize = dic['file_size']
with open(dic['file_name'],'wb') as info:
while filesize>=1024:
content = conn.recv(1024)
info.write(content)
filesize -=1024
else:
content = conn.recv(filesize)
if content:
info.write(content)
conn.close()
sk.close()
print(time.time()-start_time)
发送大文件服务端
# -*- coding: utf-8 -*-
# @Time : 2019/4/10 17:03
# @Author : p0st
# @Site :
# @File : 传递大文件client.py
# @Software: PyCharm
import json
import struct
import socket
import os
sk = socket.socket()
ip_addr = (('127.0.0.1',9999))
sk.connect_ex(ip_addr)
file_path = input("请输入文件路径:>>>").strip()
file_name = os.path.basename(file_path) #获取文件名字
file_size = os.path.getsize(file_path) #获取文件大小
s_dic = {'file_name':file_name,'file_size':file_size}
j_s_dic = json.dumps(s_dic) #将字典序列化成json
b_s_dic = j_s_dic.encode('utf-8') #将json转成bytes类脑,在网上传输
l_s_dic = len(b_s_dic) #常看bytes类型的长度
lalala = struct.pack('i',l_s_dic) #将bytes类型的长度通过struct的pack方法变成4个字节
sk.send(lalala)
sk.send(b_s_dic)
with open(file_path,'rb') as info:
while file_size >= 1024:
content = info.read(1024)
sk.send(content)
file_size -= 1024
else:
content = info.read(file_size)
if content:
sk.send(content)
sk.close()
#1.将字典转成json,再转成bytes类型,查看其长度发送给服务器端
#2.将bytes类型的发送给服务器
发送大文件客户端
使用struct解决黏包:该模块可以把一个类型,如数字,转成固定长度的bytes
我们知道长度数字可以被转换成一个标准大小的4字节数字。因此可以利用这个特点来预先发送数据长度。 发送时 接收时 先发送struct转换好的数据长度4字节 先接受4个字节使用struct转换成数字来获取要接收的数据长度 再发送数据 再按照长度接收数据
并发的socketserver
import time
import socketserver
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
conn = self.request
for i in range(200):
conn.send(('hello%s'%i).encode('utf-8'))
print(conn.recv(1024))
time.sleep(0.5)
print(conn)
server = socketserver.ThreadingTCPServer(('127.0.0.1',9001),Myserver)
server.serve_forever()
#sk.setblocking(False) #默认不阻塞,不阻塞模型,django和flask等等,setblocking做到和socketserver对tcp协议的非阻塞
# 非阻塞模型是一个突破,udp协议不用阻塞和不阻塞,udp协议能同时提供对多个客户端进行连接
python进阶之 网络编程的更多相关文章
- Python进阶之网络编程
网络通信 使用网络的目的 把多方链接在一起,进行数据传递: 网络编程就是,让不同电脑上的软件进行数据传递,即进程间通信: ip地址 ip地址概念和作用 IP地址是什么:比如192.168.1.1 这样 ...
- python高级之网络编程
python高级之网络编程 本节内容 网络通信概念 socket编程 socket模块一些方法 聊天socket实现 远程执行命令及上传文件 socketserver及其源码分析 1.网络通信概念 说 ...
- 第六篇:python高级之网络编程
python高级之网络编程 python高级之网络编程 本节内容 网络通信概念 socket编程 socket模块一些方法 聊天socket实现 远程执行命令及上传文件 socketserver及 ...
- Python进阶:函数式编程实例(附代码)
Python进阶:函数式编程实例(附代码) 上篇文章"几个小例子告诉你, 一行Python代码能干哪些事 -- 知乎专栏"中用到了一些列表解析.生成器.map.filter.lam ...
- Python之路 - 网络编程之粘包
Python之路 - 网络编程之粘包 粘包
- Python之路 - 网络编程初识
Python之路 - 网络编程初识 前言
- Python进阶之面向对象编程
面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程序设计把计算机 ...
- 周末班:Python基础之网络编程
一.楔子 你现在已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运行,你就会发现,这两个python的文件分别运行的很好.但是如果这两个程序之间想要传递一个数据, ...
- python之路——网络编程
一.楔子 你现在已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运行,你就会发现,这两个python的文件分别运行的很好.但是如果这两个程序之间想要传递一个数据, ...
随机推荐
- CNN卷积核反传分析
CNN(卷积神经网络)的误差反传(error back propagation)中有一个非常关键的的步骤就是将某个卷积(Convolve)层的误差传到前一层的池化(Pool)层上,因为在CNN中是2D ...
- 基于vue-cli配置手淘的lib-flexible + rem,实现移动端自适应
没接触过flexible的建议先看看大漠的这篇文章这样你才会知道长度为什么用rem,而字体要用px 安装flexible npm install lib-flexible --save 引入flexi ...
- FasterRCNN原理(转)
在介绍Faster R-CNN之前,先来介绍一些前验知识,为Faster R-CNN做铺垫. 一.基于Region Proposal(候选区域)的深度学习目标检测算法 Region Proposal( ...
- Oracle分析函数-keep(dense_rank first/last)
select * from criss_sales where dept_id = 'D02' order by sale_date ; 此时有个新需求,希望查看部门 D02 内,销售记录时间最早,销 ...
- [原创]Fashion汽车定位器拆解
随着共享单车的爆发增长,定位方案被彻底激活.当下主流的共享单车都采用了MTK2503的方案(后续再详细分解),本文针对某商城热卖的汽车定位器进行拆解分析. 第一部分,定位器外观. 第二部分,拆解开壳, ...
- 解决使用微软模拟器VS Emulator for Android在VS2017 Xamarin开发中不能调试程序的问题。
在使用VS2017 XAMARIN调试Android应用程序时,屏幕闪一下,进入不了调试(使用谷歌的模拟器可以调试,但是太慢), 我们现在来解决一下这个问题. 第一步:打开Hyper-V管理器 第二步 ...
- 蜕变成蝶~Linux设备驱动之DMA
如果不曾相逢 也许 心绪永远不会沉重 如果真的失之交臂 恐怕一生也不得轻松 一个眼神 便足以让心海 掠过飓风 在贫瘠的土地上 更深地懂得风景 一次远行 便足以憔悴了一颗 羸弱的心 每望一眼秋水微澜 便 ...
- 【Android Studio】Gradle配置及问题解决
Error:Failed to open zip file.Gradle's dependency cache may be corrupt (this sometimes occurs after ...
- MyCat(一) - 初体验
前提: 1.安装JDK 2.安装MySQL 3.搭建了MySQL主从 1.下载MyCat,官网:http://www.mycat.io/ wget http://dl.mycat.io/1.6-REL ...
- Java基础学习心得笔记
对于很多只会C语言的初学者而言,面对java基础语法学习,反而感觉很难,其实其中最大的问题不是语法难,而是一种编程思想的转变.面向过程就是把你的代码封装成函数,然后依次去做一件事情,面向过程是把你要做 ...