python 30 基于TCP协议的socket通信
1. 单对单循环通信
send() 和recv()不是一一对应的。
# 服务端 server.py
import socket
phone = socket.socket()
phone.bind = (('127.0.0.1', 8888)) # 绑定本地回环地址/端口号
phone.listen() # 开始监听
conn, addr = phone.accept() # 等待连接
print(f"连接{addr}成功!")
while 1:
try:
from_client_data = conn.recv(1024)
if from_client_data.upper() == b'Q': # 字节bytes Q
print('客户端已正常退出!')
break
print(f"来自客户端{addr}的消息:{from_client_data.decode('utf-8')}")
to_server_data = input('>>>').strip().encode('utf-8')
phone.send(to_server_data)
except ConnectionResetError:
print('客户端连接中断!')
break
conn.close()
phone.close()
# 客户端 client.py
import socket
phone = socket.socket()
phone.connect(('127.0.0.1',8888))
while 1:
to_server_data = input('>>>').strip().encode('utf-8')
if not to_server_data:
print('输入内容不能为空!')
continue
phone.send(to_server_data)
if to_server_data.upper() == b'Q':
break
from_server_data = phone.recv(1024)
print(f"来自服务端的消息:{from_server_data.decode('utf-8')}")
phone.close()
2. 循环连接通信:可连接多个客户端
# 服务端循环接收连接
import socket
phone = socket.socket()
phone.bind(('127.0.0.1',8848))
phone.listen()
while 1:
conn,addr = phone.accept() # 循环可接收客户端
print(f'与客户端{addr}连接成功!')
while 1:
try:
from_client_data = conn.recv(1024)
if from_client_data.upper() == b'Q':
print('客户端已退出!')
break
print(f"来自客户端的消息:{from_client_data.decode('utf-8')}")
to_client_data = input('>>>').strip().encode('utf-8')
conn.send(to_client_data)
except ConnectionResetError:
print('客户端连接中断!')
break
conn.close()
phone.close()
# 客户端 client
import socket
phone = socket.socket()
phone.connect(('127.0.0.1',8848))
while 1:
to_server_data = input('>>>').strip().encode('utf-8')
if not to_server_data:
print('输入内容不能为空!')
continue
phone.send(to_server_data)
if to_server_data.upper() == b'Q':
break
from_server_data = phone.recv(1024)
print(f"来自服务端的消息:{from_server_data.decode('utf-8')}")
phone.close
3. 执行远程命令
# 使用subproess模块 可像操作系统cmd一样执行命令
import subprocess
obj = subprocess.Popen('dir',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
print(obj.stdout.read().decode('gbk')) # 正确命令
print(obj.stderr.read().decode('gbk')) # 错误命令
# 如果是正确的命令,那么错误命令会输出空字符。
# shell: 命令解释器,相当于调用cmd 执行指定的命令。
# stdout:正确结果丢到管道中。
# stderr:错了丢到另一个管道中。
# windows操作系统的默认编码是gbk编码。
# 在服务端 增加subprocess模块,
import subprocess
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.bind(('127.0.0.1', 8888))
phone.listen()
while 1:
conn, addr = phone.accept()
print(f'连接{addr}成功!')
while 1:
try:
from_client_data = conn.recv(1024)
if from_client_data.upper() == b'Q':
print('客户端正常退出!')
break
# print(f"来自客户端{addr}的消息:{from_client_data.decode('utf-8')}")
obj = subprocess.Popen(from_client_data.decode("utf-8"),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
to_client_data = obj.stdout.read() + obj.stderr.read()
# subprocess使用当前系统默认编码,得到结果为bytes类型,在windows下需要用gbk解码
conn.send(to_client_data) # 此时是gbk的bytes的类型
except ConnectionResetError:
print('客户端连接中断!')
break
conn.close()
phone.close()
# 客户端 client
import socket
phone = socket.socket()
phone.connect(('127.0.0.1', 8888))
while 1:
to_server_data = input('>>>').strip().encode('utf-8')
if not to_server_data:
print('输入内容不能为空!')
continue
phone.send(to_server_data)
if to_server_data.upper() == b'Q':
break
from_server_data = phone.recv(1024)
print(f"{from_server_data.decode('gbk')}") # 需gbk解码
phone.close()
4. 粘包现象
4.1 socket缓冲区

每个socket,都会有两个缓冲区,输入/输出缓冲区。过程如上图,send()结束后,会马上进入recv()状态(有阻塞)。
作用:1. 暂时存储一些数据;2. 如果网络有波动,缓冲区会保护数据的收发稳定、匀速。
缺点:是会造成粘包的现象之一。
4.2 出现粘包的情况:
1. 如果客户端send() 数据超过recv() 设定的字节数,会先接收最大限制的字节数,当第二次send() 数据时,recv() 会将上次遗留的数据接收,产生粘包现象。
连续短暂send() 多次(数据量较小),数据会统一发送出去,产生粘包。
(Nagle算法)
4.3 解决粘包现象
#思路:
服务端发出send() 数据有10000字节,客户端接收数据时,循环recv()接收,每次(至多)接收1024字节,直至将所有的字节全部接收完毕,将接收的数据拼接在一起,编码打印出。
#遇到的问题:
1. recv() 次数无法确定。
客户端先接收总数据的一个长度,然后再循环recv 控制循环次数,只有接收的数据长度 < 总数据长度,就会一直接收。(先发总数据长度,再发总数据)
2.获取总数据的长度是 int 类型,先要转换成 bytes 类型,再发出,但是总数据的长度转化成的 bytes类型的字节数是不固定的。
int:376 ——> str(376):'376' ——> bytes:b'376' (长度为3)
int:4558 ——> str(4558):'4558' ——> bytes:b'4558' (长度为4) 解决:将不固定长度的int类型转换成固定长度的bytes,并且还可以反解回来。(用来制定固定长度的报头)
# 将不固定长度的int类型转换成固定长度的bytes
import struct
ret = struct.pack('i',376) # 将int 376转换成固定4个长度的bytes字节
# i 默认长度为 4; q 默认为8
print(ret,type(ret),len(ret))
ret1 = struct.unpack('i',ret)[0] # 是元组形式, 反解成int型
print(ret1,type(ret1))
# 但是通过struct 处理不能处理太大数据
ret = struct.pack('l', 4323241232132324)
print(ret, type(ret), len(ret)) # 报错
# low版解决粘包现象
# 服务端server
import socket
import subprocess
import struct
phone = socket.socket()
phone.bind(('127.0.0.1',8888))
phone.listen()
while 1:
conn, addr = phone.accept()
print(f'与客户端{addr}连接')
while 1:
try:
from_client_data = conn.recv(1024)
if from_client_data.upper() == b'Q':
print('客户端已退出!')
break
obj = subprocess.Popen(from_client_data.decode("utf-8"),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
total_data = obj.stdout.read() + obj.stderr.read()
conn.send(struct.pack('i', len(total_data))) # 发出 总数据长度的bytes类型
print(len(total_data))
conn.send(total_data) # 发出总数据 gbk编码的字节
except ConnectionResetError:
print('与客户端连接中断!')
break
conn.close()
phone.close()
# 客户端client
import socket
import struct
phone = socket.socket()
phone.connect(('127.0.0.1', 8888))
while 1:
to_server_data = input('>>>').strip().encode('utf-8')
if not to_server_data:
print('内容不能为空!')
continue
phone.send(to_server_data)
if to_server_data.upper() == b'Q':
break
head_bytes = phone.recv(4) # 接收总数据长度的bytes
head_int = struct.unpack('i', head_bytes)[0] # 反解成int
print(head_int)
data = b'' # 定义一个空字节
while len(data) < head_int:
data = data + phone.recv(1024)
print(len(data))
print(f"{data.decode('gbk')}") # 终端是 gbk 编码的
phone.close()
bytes
用于网络传输、文件存储时。

能够保持数据原本类型。
python 30 基于TCP协议的socket通信的更多相关文章
- 基于TCP协议的socket通信
一.服务器端 1.创建serverSocket,即服务器端的socket,绑定指定的端口,并侦听此端口 ServerSocket server = new ServerSocket(8888); 2. ...
- 基于TCP 协议的socket 简单通信
DNS 服务器:域名解析 socket 套接字 : socket 是处于应用层与传输层之间的抽象层,也是一组操作起来非常简单的接口(接受数据),此接口接受数据之后,交由操作系统 为什么存在 soc ...
- 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程
Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...
- day31——recv工作原理、高大上版解决粘包方式、基于UDP协议的socket通信
day31 recv工作原理 源码解释: Receive up to buffersize bytes from the socket. 接收来自socket缓冲区的字节数据, For the opt ...
- python中基于tcp协议的通信(数据传输)
tcp协议:流式协议(以数据流的形式通信传输).安全协议(收发信息都需收到确认信息才能完成收发,是一种双向通道的通信) tcp协议在OSI七层协议中属于传输层,它上承用户层的数据收发,下启网络层.数据 ...
- Java:基于TCP协议网络socket编程(实现C/S通信)
目录 一.前言:TCP原理简介 二.Socket编程通信 三.TCP服务器端(具体代码) 四.TCP客户端(具体代码) 五.通信效果演示 六."创意"机器人:价值一个亿的AI核心代 ...
- 基于TCP协议的socket套接字编程
目录 一.什么是Scoket 二.套接字发展史及分类 2.1 基于文件类型的套接字家族 2.2 基于网络类型的套接字家族 三.套接字工作流程 3.1 服务端套接字函数 3.2 客户端套接字函数 3.3 ...
- 浅析C#基于TCP协议的SCOKET通信
TCP协议是一个基本的网络协议,基本上所有的网络服务都是基于TCP协议的,如HTTP,FTP等等,所以要了解网络编程就必须了解基于TCP协议的编程.然而TCP协议是一个庞杂的体系,要彻底的弄清楚它的实 ...
- 网络编程: 基于TCP协议的socket, 实现一对一, 一对多通信
TCP协议 面向连接 可靠的 面向字节流形式的 tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端 TCP协议编码流程: 服务器端: 客户端 实例化对 ...
随机推荐
- 2019杭电多校第一场hdu6581 Vacation
Vacation 题目传送门 update(O(n)) 看了那个O(n)的方法,感觉自己想的那个O(nlogn)的好傻,awsl. 0车最终通过停车线的时候,状态一定是某个车堵住后面的所有车(这个车也 ...
- 最短代码实现包含100个key的字典,且每个value值不同
最短代码实现包含100个key的字典,且每个value值不同 {x:x*2 for x in range(100)}
- Python基础总结之第三天开始重新认识‘字符串’(新手可相互督促)
年薪20万的梦想,又进了一步... 戏好多 ’字符串‘开始啦~ 字符串的定义:字符串可以用英文单引号或双引号又或者三引号包围起来. 为毛有单引号,还要有双引号和三引号??? 看案例吧: 字符串的其他使 ...
- 基础算法和数据结构高频题 II
DFS的两种理解方式:1. 按照实际执行顺序模拟 (适合枚举型DFS,下节课内容)2. 按照DFS的定义宏观理解 (适合分治型DFS,本节课内容) 1 Convert BST to Greater T ...
- Jmeter脚本录制--HTTP代理服务器
Jmeter脚本录制功能依赖第三方工具Badboy,所以在安装了Jmeter之后,还需要再安装一个工具. Badboy本身自带浏览器,相关操作只能在Badboy上进行操作,偶尔可能会遇到浏览器兼容的问 ...
- 解决axios接收二进制流文件乱码问题
1. 须将axios 配置中的responseType设置为'arraybuffer',这样就不会让表格出现乱码现象: 2. 如果要动态设置文件名则需要让后台将名字设置到响应头中,否则将是一个乱码的文 ...
- 最火的分布式 HTAP 数据库 TiDB - 入门实践教程
偶然在某篇博客看到了 TiDB,一个融合 OLTP 和 OLAP 的分布式开源数据库, GitHub 上 Star 很多,然后 watch 了,发现 commit 和 pull request 一直都 ...
- 浅谈设计模式及python实现
设计模式及Python实现 设计模式是什么? Christopher Alexander:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心.这样你就能一次又一次地使用 ...
- c#小灶——使用visual studio编写第一个程序
虽然,写程序有文本编辑器和编译器就已经足够,但是,我们为了增加工作效率还是要使用IDE. 我们接下来所有的教程都将会在visual studio中实现,visual studio简称vs,是微软开发的 ...
- Go组件学习——gorm四步带你搞定DB增删改查
1.简介 ORM Object-Relationl Mapping, 它的作用是映射数据库和对象之间的关系,方便我们在实现数据库操作的时候不用去写复杂的sql语句,把对数据库的操作上升到对于对象的操作 ...