Python开发基础----socket套接字基础2

基于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的客户端代码做点手脚

 服务端不动代码

 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数据传送和数据接收的原理:

 原理

粘包只会出现在TCP里

UDP在windows下会提示:

  OSError: [WinError 10040] 一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小

而在linux下会出现丢数据的情况:

>>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
AAAAAAAAAA (‘192.168.1.10‘, 8080)
>>

问题出现在接收方,这是因为接收方不知道返回的消息之间的界限,不知道一次性提取多少字节的数据所造成的,第一次dir返回的消息远远大于100个字节,而懂了手脚后变成了一次只能从缓存中取100字节,再次取的时候会继续取缓存中没取完的数据

出现粘包的情况:

发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据很小,会合到一起,产生粘包)

接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

 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()

解决粘包的高大上方法-自定义数据头

该方法是基于上面方法的改良,即在传输数据之前,在服务器端定一个固定长度数据头部,该数据头部封装了一系列关于该数据的信息,如数据的总长度,或者传输文件数据的用户信息、时间信息等等,客户端取得整个数据的时候,先取固定长度的数据头部读取信息,按照头部信息接收数据

时间: 2024-10-05 11:32:14

Python开发基础----socket套接字基础2的相关文章

Python开发基础-Day23try异常处理、socket套接字基础1

异常处理 错误 程序里的错误一般分为两种: 1.语法错误,这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正 2.逻辑错误,人为造成的错误,如数据类型错误.调用方法错误等,这些解释器是不会进行检测的,只有在执行的过程中才能抛出的错误 异常 异常是python解释器在运行程序的过程中遇到错误所抛出的信息,如: Python异常种类: 常用异常: 1 AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x 2 IOError 输入/输出异

Python开发基础----异常处理、socket套接字基础1

异常处理 错误 程序里的错误一般分为两种: 1.语法错误,这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正 2.逻辑错误,人为造成的错误,如数据类型错误.调用方法错误等,这些解释器是不会进行检测的,只有在执行的过程中才能抛出的错误 异常 异常是python解释器在运行程序的过程中遇到错误所抛出的信息,如: Python异常种类: 常用异常: 1 AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x 2 IOError 输入/输出异

python基础之try异常处理、socket套接字基础part1

异常处理 错误 程序里的错误一般分为两种: 1.语法错误,这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正 2.逻辑错误,人为造成的错误,如数据类型错误.调用方法错误等,这些解释器是不会进行检测的,只有在执行的过程中才能抛出的错误 异常 异常是python解释器在运行程序的过程中遇到错误所抛出的信息,如: Python异常种类: 常用异常: 1 AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x 2 IOError 输入/输出异

Python开发基础-Day24socket套接字基础2

基于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('>>').str

Python网络编程—socket套接字编程(UDP)

套接字介绍 1.套接字 : 实现网络编程进行数据传输的一种技术手段 2.Python实现套接字编程:import socket 3.套接字分类 流式套接字(SOCK_STREAM): 以字节流方式传输数据,实现tcp网络传输方案.(面向连接--tcp协议--可靠的--流式套接字) 数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现udp网络传输方案.(无连接--udp协议--不可靠--数据报套接字) UDP套接字编程 服务端流程 1.创建数据报套接字 sockfd = socket

Python网络编程—socket套接字编程(TCP)

套接字介绍 1.套接字 : 实现网络编程进行数据传输的一种技术手段 2.Python实现套接字编程:import socket 3.套接字分类 流式套接字(SOCK_STREAM): 以字节流方式传输数据,实现tcp网络传输方案.(面向连接--tcp协议--可靠的--流式套接字) 数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现udp网络传输方案.(无连接--udp协议--不可靠--数据报套接字) tcp套接字 服务端流程 1.创建套接字 sockfd=socket.socket

python基础--socket套接字、粘包问题

本地回环地址:127.0.0.1 简易版服务端: import socket ? server = socket.socket() # 就比如买了一个手机 server.bind(("127.0.0.1",8080)) # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机 server.listen(5) # 半连接池,最大等待连接数为5个,就比如开机 conn,address = server.accept() # 接听电话等着别人给你打电话 ? da

[python] 网络编程之套接字Socket、TCP和UDP通信实例

很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send.Receive方法的同步通讯 Python网络编程也类似.同时最近找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. 最后希望文章对你有所帮助,如果有不

原始套接字基础(原始套接字系列二)

在进入Raw Socket多种强大的应用之前,我们先讲解怎样建立一个Raw Socket及怎样用建立的Raw Socket发送和接收IP包. 建立Raw Socket 在Windows平台上,为了使用Raw Socket,需先初始化WINSOCK: // 启动 WinsockWSAData wsaData;if (WSAStartup(MAKEWORD(2, 1), &wsaData) != 0){ cerr << "Failed to find Winsock 2.1 or