11-Python网络编程
socket包介绍
Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
创建一个TCP协议的socket:
socket.socket()
创建一个UDP协议的socket:
socket.socket(type = socket.SOCK_DGRAM)
socket包的特点:
- 使用bytes类型发送和接收数据。
- 接收数据时:linux下若连接关闭,则接收空;Windows下连接关闭,则报错。
发送数据:
send() #可能一次不会发送完(超过MTU)
sendall() #循环调用send()将数据发送完(一般使用sendall即可)
sendto(msg1,client_addr) #发送数据时指定对端地址
接收数据:
recv(1500) #接收数据(最多1500字节)
recvfrom(1024) #不仅能接收数据,还能获取对方ip(适用于未知ip的情况)
使用TCP传输数据
服务端:
import socket
sk = socket.socket() #创建套接字对象
address = ('127.0.0.1', 8000)
sk.bind(address) #绑定监听IP和端口
sk.listen(3) # 最大连接数(超过则拒绝)
conn, addr = sk.accept() #接收连接(返回一个元组)
conn.sendall(bytes("你好", "utf8")) #发送数据(转为bytes发送)
s = conn.recv(1024) #接收数据,最大接收1024B数据
print(str(s, "utf8")) # 转为UTF-8输出
conn.close()
sk.close()
客户端:
import socket
sk = socket.socket() #创建套接字对象
address = ('127.0.0.1', 8000)
sk.connect(address) #连接到服务端
sk.sendall(bytes("你好", "utf8")) #发送数据(转为bytes发送)
s = sk.recv(1024) #接收数据,最大接收1024B数据
print(str(s, "utf8")) #转为UTF-8输出
sk.close()
解释:
- 发送和接收的顺序无要求
- sk.accept()和conn.close()包含三次握手和四次挥手,UDP协议是没有的。
使用UDP传输数据
服务端:
import socket
sk = socket.socket(type = socket.SOCK_DGRAM) #UDP协议必须加上 type=socket.SOCK_DGRAM
sk.bind(('127.0.0.1',9000))
msg,client_addr = sk.recvfrom(1024) #recvfrom用于不知道对方ip时,获取到ip给client_addr
msg = msg.decode('utf-8')
print(msg)
msg1 = input('>>>').encode('utf-8') #输入数据
sk.sendto(msg1,client_addr)
sk.close()
客户端:
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
inp = input('>>>').encode('utf-8') #输入数据
sk.sendto(inp, ('127.0.0.1', 9000))
msg = sk.recv(1024).decode('utf-8') #这里不需要使用recvfrom,因为知道对方ip地址
print(msg)
sk.close()
粘包
粘包:原本应该分离的多个数据包,变成了一个包
发送方粘包:缓冲区中存在多个数据包,合成一起发送
接收方粘包:来不及处理缓冲区中的包,多个数据包一起处理
因为算法原因,TCP会产生粘包现象,而UDP永远不会。
粘包现象
服务端:
import socket
sk = socket.socket()
sk.bind(('127.0.0.1', 8888))
sk.listen()
conn, addr = sk.accept()
while 1:
conn.send(b'hello')
conn.send(b'world')
conn.close()
sk.close()
客户端:
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 8888))
while 1:
msg1 = sk.recv(1024) #接收服务器返回来的数据
print('msg1:', msg1)
msg2 = sk.recv(1024)
print('msg2:', msg2)
sk.close()
结果:
msg1: b'hello'
msg2: b'world'
msg1: b'helloworld' #产生粘包现象
msg2: b'hello'
msg1: b'world'
msg2: b'hello'
msg1: b'world'
msg2: b'hello'
msg1: b'worldhello' #产生粘包现象
msg2: b'world'
msg1: b'hello'
msg2: b'world'
结果:
连续发送和接收数据,就有可能产生粘包。
解决粘包问题-struct模块
解决思路:
- 发送方:先发送一个头部,告诉接收端应该接收多少数据,再发送真实数据。
- 接收方:先接收一个头部,再根据头部信息,接收相应字节的数据。
struct模块中的方法简介:
pack(fmt, v1, v2, ...) #按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)
unpack(fmt, string) #按照给定的格式(fmt)解析字节流string,返回解析出来的tuple
calcsize(fmt) #计算给定的格式(fmt)占用多少字节的内存
示例:
size = struct.pack('i', 50) #将整数50转化为4个字节的struct类型
格式(上例中的fmt)
| Format | C Type | Python | 字节数 |
|---|---|---|---|
| x | pad byte | no value | 1 |
| c | char | string of length 1 | 1 |
| b | signed char | integer | 1 |
| B | unsigned char | integer | 1 |
| ? | _Bool | bool | 1 |
| h | short | integer | 2 |
| H | unsigned short | integer | 2 |
| i | int | integer | 4 |
| I | unsigned int | integer or long | 4 |
| l | long | integer | 4 |
| L | unsigned long | long | 4 |
| q | long long | long | 8 |
| Q | unsigned long long | long | 8 |
| f | float | float | 4 |
| d | double | float | 8 |
| s | char[] | string | 1 |
利用struct模块解决粘包问题
服务端:
import socket,struct
sk = socket.socket()
sk.bind(('127.0.0.1', 8888))
sk.listen()
conn, addr = sk.accept()
while 1:
s = b'hello'
size = struct.pack('i', len(s))
conn.send(size) # 先发送头部
conn.send(s) # 再发送真实数据
s = b'world'
size = struct.pack('i', len(s))
conn.send(size) # 先发送头部
conn.send(s) # 再发送真实数据
conn.close()
sk.close()
客户端:
import socket,struct
sk = socket.socket()
sk.connect(('127.0.0.1', 8888))
while 1:
size = sk.recv(4) #先接收头部
size = struct.unpack('i', size)[0] #转化为整数
msg1 = sk.recv(size).decode("UTF-8")#接收真实数据
print('msg1:', msg1)
size = sk.recv(4) #先接收头部
size = struct.unpack('i', size)[0] #转化为整数
msg2 = sk.recv(size).decode("UTF-8")#接收真实数据
print('msg2:', msg2)
sk.close()
聊天小程序
服务端:
# server.py 服务端
import socket
ip_port=('127.0.0.1',8081)
udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #DGRAM:datagram 数据报文的意思,象征着UDP协议的通信方式
udp_server_sock.bind(ip_port) #你对外提供服务的端口就是这一个,所有的客户端都是通过这个端口和你进行通信的
while True:
qq_msg,addr=udp_server_sock.recvfrom(1024)# 阻塞状态,等待接收消息
print('来自[%s:%s]的一条消息:\033[1;34;43m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))
back_msg=input('回复消息: ').strip()
udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
udp_server_sock.recv(0)
客户端:
import socket
BUFSIZE=1024
udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
qq_name_dic={
'taibai':('127.0.0.1',8081),
'Jedan':('127.0.0.1',8081),
'Jack':('127.0.0.1',8081),
'John':('127.0.0.1',8081),
}
while True:
while 1:
qq_name=input('请选择聊天对象: ').strip()
if qq_name not in qq_name_dic:
print('没有这个聊天对象')
continue
break
while True:
msg=input('请输入消息,回车发送,输入q结束和他的聊天: ').strip()
if msg == 'q':
break
msg = '发给' + qq_name + ': ' + msg
if not msg:continue
udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])# 必须带着自己的地址,这就是UDP不一样的地方,不需要建立连接,但是要带着自己的地址给服务端,否则服务端无法判断是谁给我发的消息,并且不知道该把消息回复到什么地方,因为我们之间没有建立连接通道
udp_client_socket.recv(0)
back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)# 同样也是阻塞状态,等待接收消息
print('来自[%s:%s]的一条消息:\033[1;34;43m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
udp_client_socket.close()
11-Python网络编程的更多相关文章
- Python 网络编程(二)
Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单 ...
- Python 网络编程(一)
Python 网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. ...
- python网络编程——IO多路复用之select
1 IO多路复用的概念 原生socket客户端在与服务端建立连接时,即服务端调用accept方法时是阻塞的,同时服务端和客户端在收发数据(调用recv.send.sendall)时也是阻塞的.原生so ...
- python网络编程05 /TCP阻塞机制
python网络编程05 /TCP阻塞机制 目录 python网络编程05 /TCP阻塞机制 1.什么是拥塞控制 2.拥塞控制要考虑的因素 3.拥塞控制的方法: 1.慢开始和拥塞避免 2.快重传和快恢 ...
- Python网络编程基础|百度网盘免费下载|零基础入门学习资料
百度网盘免费下载:Python网络编程基础|零基础学习资料 提取码:k7a1 目录: 第1部分 底层网络 第1章 客户/服务器网络介绍 第2章 网络客户端 第3章 网络服务器 第4章 域名系统 第5章 ...
- Python网络编程基础 PDF 完整超清版|网盘链接内附提取码下载|
点此获取下载地址提取码:y9u5 Python网络编程最好新手入门书籍!175个详细案例,事实胜于雄辩,Sockets.DNS.Web Service.FTP.Email.SMTP.POP.IMAP. ...
- Python网络编程之网络基础
Python网络编程之网络基础 目录 Python网络编程之网络基础 1. 计算机网络发展 1.1. OSI七层模型 1.2. 七层模型传输数据过程 2. TCP/IP协议栈 2.1 TCP/IP和O ...
- Python学习(22)python网络编程
Python 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的 ...
- Day07 - Python 网络编程 Socket
1. Python 网络编程 Python 提供了两个级别访问网络服务: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...
- python网络编程-01
python网络编程 1.socket模块介绍 ①在网络编程中的一个基本组件就是套接字(socket),socket是两个程序之间的“信息通道”. ②套接字包括两个部分:服务器套接字.客户机套接字 ③ ...
随机推荐
- [Contract] Solidity 多种访问控制 (Access Control) 实现方式
在 solidity 中控制访问,一般是通过 modifier 修饰符方法来直接做. 那么对于稍复杂的多种访问控制,通常需要一个统一操作的模块化类库. 现在已经有了这样的类库存在,我们通过一个实现功能 ...
- 迁移 dotnet 6 提示必须将目标平台设置为 Windows 平台
我在迁移一个古老的项目为 .NET 6 框架,但是 VS 提示 error NETSDK1136 如果使用 Windows 窗体或 WPF,或者引用使用 Windows 窗体或 WPF 的项目或包,则 ...
- MSSQL—存储过程分页
SET QUOTED_IDENTIFIER ON SET ANSI_NULLS ON GO CREATE PROCEDURE [dbo].[GetPagingStr] @PRESQL VARCHAR( ...
- Linux 环境下制作 deb 软件包
一.简介 前面的笔记中已经展示过了,怎么移植的一个工具境到 ARM 环境中,对于使用 buildroot 和 yocto 的朋友来说,此笔记就没有作用了,因为管理工具包会帮我们把这个工作处理了,就算需 ...
- CF746 期望+逆序对
Link 题意:给定一个 \(1\) 到 \(n\) 的排列,等概率选一段区间 \([l, r]\) 随机排序,求期望逆序对数. \[E = \dfrac{\sum(cnt_{[1, n]} - cn ...
- rpc 和 http的区别
- python教程2:if...else...+循环
一.if判断 有单分支.双分支.多分支,下面就是一个多分支的案例: 二.缩进 三.for循环 四.while循环 五.其他 random模块 string模块
- Visual Studio中的四款代码格式化工具
前言 今天大姚给大家分享四款Visual Studio中的代码格式化工具.扩展插件.大家可以在Visual Studio中的管理扩展或者插件市场下载安装. 代码格式化工具的作用 自动调整代码的布局和风 ...
- C数据结构:树和森林存储方式与遍历方式
文章目录 树的存储方式 双亲表示法 孩子链表表示法 孩子兄弟表示法(二叉树表示法) 树和二叉树的转换 森林和二叉树的转换 树和森林的遍历 树的遍历方式 森林的遍历方式 浅谈一下几个问题 为什么树没有中 ...
- echarts(数据可视化图表)
echarts饼图详细 echarts下载 https://echarts.apache.org/zh/index.html echarts官网 http://www.isqqw.com/#/hom ...