python基础之socket套接字基础part2
基于UDP的socket
面向无连接的不可靠数据传输,可以没有服务器端,只不过没有服务器端,发送的数据会被直接丢弃,并不能到达服务器端
1 #客户端
2 import socket
3 ip_port=('127.0.0.1',8080)
4 BUFSIZE=1024
5 sock_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #SOCK_DGRAM就是UDP
6 while True:
7 msg=input('>>').strip()
8 if not msg:continue
9 sock_client.sendto(msg.encode('utf-8'),ip_port) #UDP用的是sendto发送数据
UDP服务端+客户端
1 #服务端
2 import socket
3 ip_port=('127.0.0.1',8080)
4 BUFSIZE=1024
5 sock_server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
6 sock_server.bind(ip_port)
7 #对比TCP,缺少listen侦听地址,缺少accept等待连接的代码
8 while True:
9 msg,addr=sock_server.recvfrom(BUFSIZE) #UDP接收数据使用recvfrom接收
10 print('recv:',msg,addr)
11 sock_server.sendto(msg.upper(),addr)
12
13 #客户端
14 import socket
15 ip_port=('127.0.0.1',8080)
16 BUFSIZE=1024
17 sock_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
18 while True:
19 msg=input('>>').strip()
20 if not msg:continue
21 sock_client.sendto(msg.encode('utf-8'),ip_port)
22 # back_msg,addr=sock_client.recvfrom(BUFSIZE) #一般UDP用于广播,不会接收数据,如果没有服务端,启用该行代码会出错
23 # print(back_msg.decode('utf-8'),addr)
由于UDP是面向无连接的(实际上有链接,不然通过什么去传数据去取数据),可以使用多个客户端连接服务端,但这并不是并发访问。
注意:
1. 发消息,都是将数据发送到己端的发送缓冲中,收消息都是从己端的缓冲区中收
tcp:send发消息,recv收消息
udp:sendto发消息,recvfrom收消息
2. tcp是基于数据流的,而udp是基于数据报的:
send(bytes_data):发送数据流,数据流bytes_data若为空,自己这段的缓冲区也为空,操作系统不会控制tcp协议发空包
sendinto(bytes_data,ip_port):发送数据报,bytes_data为空,还有ip_port,所有即便是发送空的bytes_data,数据报其实也不是空的,自己这端的缓冲区收到内容,操作系统就会控制udp协议发包。
3.1 tcp协议
(1)如果收消息缓冲区里的数据为空,那么recv就会阻塞(阻塞很简单,就是一直在等着收)
(2)只不过tcp协议的客户端send一个空数据就是真的空数据,客户端即使有无穷个send空,也跟没有一个样。
(3)tcp基于链接通信
- 基于链接,则需要listen(backlog),指定半连接池的大小
- 基于链接,必须先运行的服务端,然后客户端发起链接请求
- 对于mac系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端在收消息后加上if判断,空消息就break掉通信循环)
- 对于windows/linux系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端通信循环内加异常处理,捕捉到异常后就break掉通讯循环)
3.2 udp协议
(1)如果如果收消息缓冲区里的数据为“空”,recvfrom也会阻塞
(2)只不过udp协议的客户端sendinto一个空数据并不是真的空数据(包含:空数据+地址信息,得到的报仍然不会为空),所以客户端只要有一个sendinto(不管是否发送空数据,都不是真的空数据),服务端就可以recvfrom到数据。
(3)udp无链接
- 无链接,因而无需listen(backlog),更加没有什么连接池之说了
- 无链接,udp的sendinto不用管是否有一个正在运行的服务端,可以己端一个劲的发消息,只不过数据丢失
- recvfrom收的数据小于sendinto发送的数据时,在mac和linux系统上数据直接丢失,在windows系统上发送的比接收的大直接报错
- 只有sendinto发送数据没有recvfrom收数据,数据丢失
粘包
对昨天ssh的客户端代码做点手脚
import socket
import subprocess
ssh_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #生成socket实例对象
ssh_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #重用地址,防止占用
ssh_server.bind(('127.0.0.1',8080))
ssh_server.listen(5)
print('server run...')
while True:
conn,client_addr=ssh_server.accept() #循环等待连接
print('客户端: ',client_addr)
while True: #通讯循环
try:
cmd=conn.recv(1024) #收消息
res=subprocess.Popen(cmd.decode('utf-8'), #执行命令
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout=res.stdout.read() #标准输出
stderr=res.stderr.read() #标准输入
std=stdout+stderr
conn.sendall(std) except Exception:
break
conn.close() #断连接,进入下一次连接等待
ssh_server.close() #关闭程序 服务端不动代码
服务端不动代码
1 #客户端动手脚
2 import socket
3 ssh_client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
4 ssh_client.connect(('127.0.0.1',8080))
5 while True: #通讯循环
6 cmd=input('>>: ').strip()
7 if not cmd:continue
8 ssh_client.send(cmd.encode('utf-8'))
9 cmd_res = ssh_client.recv(100) #动手脚位置,将一次接收的数据大小改为100字节
10 print(cmd_res.decode('gbk')) #windows
11 # print(cmd_res.decode('utf-8')) #linux
12 ssh_client.close()
运行服务端后,执行客户端测试:
1 >>: dir
2 驱动器 C 中的卷没有标签。
3 卷的序列号是 5E42-F448
4
5 C:\Users\Mr.chai\Desktop\PythonProject\笔记\
6 >>: pwd
7 2017.7.10\套接字_test 的目录
8
9 2017/07/11 16:58 <DIR> .
10 2017/07/11 16:58 <DIR>
11 >>: pwd
12 ..
13 2017/07/10 11:04 0 __init__.py
14 2017/07/11 16:58 711 客户
15 >>: pwd
16 端.py
17 2017/07/11 16:03 1,992 服务端.py
18 3 个文件 2,703 字节
19
20 >>: pwd
21 2 个目录 42,335,735,808 可用字节
22 'pwd' 不是内部或外部命令,也不是可运行的程序
23 或批处
24 >>:
对比没动手脚之前:
1 >>: dir
2 驱动器 C 中的卷没有标签。
3 卷的序列号是 5E42-F448
4
5 C:\Users\Mr.chai\Desktop\PythonProject\笔记\2017.7.10\套接字_test 的目录
6
7 2017/07/11 17:02 <DIR> .
8 2017/07/11 17:02 <DIR> ..
9 2017/07/10 11:04 0 __init__.py
10 2017/07/11 17:02 712 客户端.py
11 2017/07/11 16:03 1,992 服务端.py
12 3 个文件 2,704 字节
13 2 个目录 42,335,076,352 可用字节
14
15 >>: pwd
16 'pwd' 不是内部或外部命令,也不是可运行的程序
17 或批处理文件。
18
19 >>:
What happened?
发生了什么事?原因是这个样子。。
首先是socket数据传送和数据接收的原理:
发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。怎样定义消息呢?可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候,无论底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。 例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束 所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。 此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。 TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略
udp的recvfrom是阻塞的,一个recvfrom(x)必须对一个一个sendinto(y),收完了x个字节的数据就算完成,若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠 tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。 原理
原理
粘包只会出现在TCP里
UDP在windows下会提示:
OSError: [WinError 10040] 一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小
而在linux下会出现丢数据的情况:
>>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
AAAAAAAAAA ('192.168.1.10', 8080)
>>
问题出现在接收方,这是因为接收方不知道返回的消息之间的界限,不知道一次性提取多少字节的数据所造成的,第一次dir返回的消息远远大于100个字节,而懂了手脚后变成了一次只能从缓存中取100字节,再次取的时候会继续取缓存中没取完的数据
出现粘包的情况:
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据很小,会合到一起,产生粘包)
接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
send(字节流)和recv(1024)及sendall
recv里指定的1024意思是从缓存里一次拿出1024个字节的数据
send的字节流是先放入己端缓存,然后由协议控制将缓存内容发往对端,如果待发送的字节流大小大于缓存剩余空间,那么数据丢失,用sendall就会循环调用send,数据不会丢失
send(字节流)和recv(1024)及sendall
解决粘包的lowB方法
粘包的根源是接收端不知道发送端将要传送的字节流的长度,那么接收端提前把自己要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据
服务端
1 import socket
2 import subprocess
3 ip_addr=('127.0.0.1',8088)
4 BUFSIZE=1024
5 s_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
6 s_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
7 s_server.bind(ip_addr)
8 s_server.listen(5)
9 print('run server...')
10
11 while True:
12 conn,addr=s_server.accept()
13 print('客户端地址:',addr)
14 while True:
15 try:
16 client_res=conn.recv(BUFSIZE)
17 if len(client_res.decode('utf-8')) == 0:continue
18 res=subprocess.Popen(client_res.decode('utf-8'),
19 shell=True,
20 stdout=subprocess.PIPE,
21 stderr=subprocess.PIPE)
22 stdout=res.stdout.read()
23 stderr=res.stderr.read()
24 std_bytes=stdout+stderr #标准输出和标准错误组合
25 std_size=len(std_bytes) #计算总长度
26 conn.send(str(std_size).encode('utf-8')) #将总长度发给客户端,客户端收到该消息返回一个状态
27 status=conn.recv(BUFSIZE).decode('utf-8') #将返回来的状态赋值
28 if status: #如果该状态成立,那么开始发送所有数据
29 conn.send(std_bytes)
30 except Exception:
31 break
32 conn.close()
33 s_server.close()
客户端
1 import socket
2 ip_addr=('127.0.0.1',8088)
3 BUFSIZE=100
4 s_client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
5 s_client.connect(ip_addr)
6 while True:
7 cmd=input('>>').strip()
8 if not cmd:continue
9 s_client.send(cmd.encode('utf-8'))
10
11 std_size=int(s_client.recv(BUFSIZE).decode('utf-8')) #将接收的数据总长度转换成数字
12 s_client.send('True'.encode('utf-8')) #返回给服务器端一个状态True
13 res=b''
14 get_size=0
15 while get_size < std_size:
16 if (std_size-get_size) < 100: #如果总长度比下载的长度小于定义的100,那么就取数据的最小值,否则按100取值
17 res+=s_client.recv(std_size-get_size)
18 else:
19 res+=s_client.recv(BUFSIZE)
20 get_size+=BUFSIZE #每取一次值加100,最后一次的值肯定大于总长度
21 print(res.decode('gbk'))
22 s_client.close()
解决粘包的高大上方法-自定义数据头
该方法是基于上面方法的改良,即在传输数据之前,在服务器端定一个固定长度数据头部,该数据头部封装了一系列关于该数据的信息,如数据的总长度,或者传输文件数据的用户信息、时间信息等等,客户端取得整个数据的时候,先取固定长度的数据头部读取信息,按照头部信息接收数据
python基础之socket套接字基础part2的更多相关文章
- Python进阶----SOCKET套接字基础, 客户端与服务端通信, 执行远端命令.
Python进阶----SOCKET套接字基础, 客户端与服务端通信, 执行远端命令. 一丶socket套接字 什么是socket套接字: 专业理解: socket是应用层与TCP/IP ...
- Python开发基础-Day23try异常处理、socket套接字基础1
异常处理 错误 程序里的错误一般分为两种: 1.语法错误,这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正 2.逻辑错误,人为造成的错误,如数据类型错误.调用方法错误等,这些解 ...
- python基础之try异常处理、socket套接字基础part1
异常处理 错误 程序里的错误一般分为两种: 1.语法错误,这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正 2.逻辑错误,人为造成的错误,如数据类型错误.调用方法错误等,这些解 ...
- 网络编程基础之Socket套接字简单应用
一.Socket套接字实现通信循环 所谓通信循环,简单理解就是客户端可以给服务端循环发送信息并获得反馈的过程. 1.基础版 通信循环的程序分为两部分,即两个python模块,分别为客户端.py和服务端 ...
- 网络编程基础之Socket套接字
一.Socket介绍 1.什么是socket? Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族 ...
- python网络编程-socket套接字通信循环-粘包问题-struct模块-02
前置知识 不同计算机程序之间数据的传输 应用程序中的数据都是从程序所在计算机内存中读取的. 内存中的数据是从硬盘读取或者网络传输过来的 不同计算机程序数据传输需要经过七层协议物理连接介质才能到达目标程 ...
- 网络编程基础之socket套接字编程实现同一IP下的信息传输
鲁照山 1.网络协议的5层模型,每层内容的整理 2.画图描述三次握手四次挥手,和C端S端的状态 3.写一个客户端,实现给服务端发送hello world 字符串, 写一个服务端,将客户端发送的数据变成 ...
- Python Web学习笔记之socket套接字
套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象.它们允许程序接受并进行连接,如发送和接受数据.为了建立通信通道,网络通信 ...
- python 29 Socket - 套接字
目录 Socket - 套接字 Socket - 套接字 应用层与TCP/IP协议族通信层(或传输层)之间的抽象层,是一组接口()接收数据:当接口接收数据之后,交由操作系统: 如果数据与操作系统直接交 ...
随机推荐
- Sendip 命令行发包工具,支持IP、TCP、UDP等
Sendip是一个linux平台的命令行发数据包工具,目前(2018年2月)支持的协议有ipv4.ipv6.icmp.tcp.udp.bgp.rip.ntp,作者表示其他协议将会后面支持,当他有空写的 ...
- python常用模块(一)
#什么是模块呢?就是用一大坨代码来完成一个功能的代码集合,是不是简单易懂 #类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个 ...
- 什么是SWP文件?能否删除swp文件?
SWP意思就是交换文件..SWP是各种操作系统(Windows或Linux)使用的交换文件的文件扩展名. 可以安全地清理SWP文件以释放磁盘空间. 要清理SWP文件,请按照以下步骤操作: 运行WinU ...
- Ubuntu 12.04 安装Chrome步骤
一.添加PPA 从Google Linux Repository(http://www.google.com/linuxrepositories/)下载安装Key,或把下面的代码复制进终端,回车,需要 ...
- SQLServer用存储过程实现插入更新数据
实现 1)有同样的数据,直接返回(返回值:0): 2)有主键同样,可是数据不同的数据,进行更新处理(返回值:2): 3)没有数据,进行插入数据处理(返回值:1). [创建存储过程] Create pr ...
- CodeForces 30C Shooting Gallery 简单dp
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/qq574857122/article/details/36944227 题目链接:点击打开链接 给定 ...
- 3大框架Struts、Hibernate、Spring简单了解
3大框架:Struts.Hibernate.Spring 基本概念:Spring/Struts/Hibernate是干嘛用的? 三个框架产生的技术历史背景 学习前首先应该掌握的基础知识 学习一个开发框 ...
- 【luogu P2194 HXY烧情侣】 题解
题目链接:https://www.luogu.org/problemnew/show/P2194 第一问:缩点并且统计其强连通分量里的最小耗费.把所有强连通分量的最小耗费加起来. 第二问:统计在每个强 ...
- Android学习笔记_33_Widget时钟(MetaData)
Widgets在文档docs\guide\topics\appwidgets\index.html下 Android中AppWidget的分析与应用:AppWidgetProvider 一.在 And ...
- Extjs 4 动态显示折线图 按秒显示
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...