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的文件分别运行的很好.但是如果这两个程序之间想要传递一个数据, ...
随机推荐
- centos6 通过 kvm 安装 centos7
参考: http://blog.51cto.com/wzlinux/1731324 http://blog.csdn.net/ztynet/article/details/54952425 一.检查C ...
- 你可能不知道的IDEA高级调试技巧
一.条件断点 循环中经常用到这个技巧,比如:遍历1个大List的过程中,想让断点停在某个特定值. 参考上图,在断点的位置,右击断点旁边的小红点,会出来一个界面,在Condition这里填入断点条件即可 ...
- docker的/var/lib/docker/overlay文件夹突然占满了硬盘
场景描述 事情是这样的,前两天客户的服务器突然断电了,导致用docker部署的服务也挂了.昨天去现场,重启了docker,同时准备更新一下服务.结果发现有一台节点硬盘满了- 还是觉得有些奇怪的,毕竟d ...
- 我们为什么要在Android中使用RxJava
本文翻译来自–>Why should we use RxJava on Android 另外: 微凉一季 再另外: 微凉一季 感觉RxJava近期风生水起,不学习一下都不好意思了.洒家也是初学R ...
- C++实现景区信息管理系统
景区信息管理系统 实现了: 1.1 建立主程序应用菜单选项 主程序应用菜单选项包含所实现的所有功能,并且对选项采用数字标识进行选择,对其他错误输入可以进行判别,提示输入错误. 1.2 导游线路图的创建 ...
- K好数(DP)
问题描写叙述 假设一个自然数N的K进制表示中随意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数. 求L位K进制数中K好数的数目. 比如K = 4,L = 2的时候.全部K好数为11.13.2 ...
- game 角色相关记录
GameServer启动 (role, misc, mail, offline)从共享内存中加载数据到m_mBlob中如果共享内存没有则从DB加载 主要是修改了同步共享内存,共享内存同步数据库{//r ...
- LeetCode - 868. Binary Gap
Given a positive integer N, find and return the longest distance between two consecutive 1's in the ...
- HTTP 请求头 WIKI 地址
https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
- 【转载】C++中替代sprintf的std::ostringstream输出流详解
一.简单介绍 ostringstream是C++的一个字符集操作模板类,定义在sstream.h头文件中.ostringstream类通常用于执行C风格的串流的输出操作,格式化字符串,避免申请大量的缓 ...