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() 会将上次遗留的数据接收,产生粘包现象。

  1. 连续短暂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通信的更多相关文章

  1. 基于TCP协议的socket通信

    一.服务器端 1.创建serverSocket,即服务器端的socket,绑定指定的端口,并侦听此端口 ServerSocket server = new ServerSocket(8888); 2. ...

  2. 基于TCP 协议的socket 简单通信

    DNS 服务器:域名解析 socket 套接字 : ​ socket 是处于应用层与传输层之间的抽象层,也是一组操作起来非常简单的接口(接受数据),此接口接受数据之后,交由操作系统 为什么存在 soc ...

  3. 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程

    Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...

  4. day31——recv工作原理、高大上版解决粘包方式、基于UDP协议的socket通信

    day31 recv工作原理 源码解释: Receive up to buffersize bytes from the socket. 接收来自socket缓冲区的字节数据, For the opt ...

  5. python中基于tcp协议的通信(数据传输)

    tcp协议:流式协议(以数据流的形式通信传输).安全协议(收发信息都需收到确认信息才能完成收发,是一种双向通道的通信) tcp协议在OSI七层协议中属于传输层,它上承用户层的数据收发,下启网络层.数据 ...

  6. Java:基于TCP协议网络socket编程(实现C/S通信)

    目录 一.前言:TCP原理简介 二.Socket编程通信 三.TCP服务器端(具体代码) 四.TCP客户端(具体代码) 五.通信效果演示 六."创意"机器人:价值一个亿的AI核心代 ...

  7. 基于TCP协议的socket套接字编程

    目录 一.什么是Scoket 二.套接字发展史及分类 2.1 基于文件类型的套接字家族 2.2 基于网络类型的套接字家族 三.套接字工作流程 3.1 服务端套接字函数 3.2 客户端套接字函数 3.3 ...

  8. 浅析C#基于TCP协议的SCOKET通信

    TCP协议是一个基本的网络协议,基本上所有的网络服务都是基于TCP协议的,如HTTP,FTP等等,所以要了解网络编程就必须了解基于TCP协议的编程.然而TCP协议是一个庞杂的体系,要彻底的弄清楚它的实 ...

  9. 网络编程: 基于TCP协议的socket, 实现一对一, 一对多通信

    TCP协议  面向连接 可靠的 面向字节流形式的 tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端 TCP协议编码流程: 服务器端:                 客户端 实例化对 ...

随机推荐

  1. 2019杭电多校第一场hdu6581 Vacation

    Vacation 题目传送门 update(O(n)) 看了那个O(n)的方法,感觉自己想的那个O(nlogn)的好傻,awsl. 0车最终通过停车线的时候,状态一定是某个车堵住后面的所有车(这个车也 ...

  2. 最短代码实现包含100个key的字典,且每个value值不同

    最短代码实现包含100个key的字典,且每个value值不同 {x:x*2 for x in range(100)}

  3. Python基础总结之第三天开始重新认识‘字符串’(新手可相互督促)

    年薪20万的梦想,又进了一步... 戏好多 ’字符串‘开始啦~ 字符串的定义:字符串可以用英文单引号或双引号又或者三引号包围起来. 为毛有单引号,还要有双引号和三引号??? 看案例吧: 字符串的其他使 ...

  4. 基础算法和数据结构高频题 II

    DFS的两种理解方式:1. 按照实际执行顺序模拟 (适合枚举型DFS,下节课内容)2. 按照DFS的定义宏观理解 (适合分治型DFS,本节课内容) 1 Convert BST to Greater T ...

  5. Jmeter脚本录制--HTTP代理服务器

    Jmeter脚本录制功能依赖第三方工具Badboy,所以在安装了Jmeter之后,还需要再安装一个工具. Badboy本身自带浏览器,相关操作只能在Badboy上进行操作,偶尔可能会遇到浏览器兼容的问 ...

  6. 解决axios接收二进制流文件乱码问题

    1. 须将axios 配置中的responseType设置为'arraybuffer',这样就不会让表格出现乱码现象: 2. 如果要动态设置文件名则需要让后台将名字设置到响应头中,否则将是一个乱码的文 ...

  7. 最火的分布式 HTAP 数据库 TiDB - 入门实践教程

    偶然在某篇博客看到了 TiDB,一个融合 OLTP 和 OLAP 的分布式开源数据库, GitHub 上 Star 很多,然后 watch 了,发现 commit 和 pull request 一直都 ...

  8. 浅谈设计模式及python实现

    设计模式及Python实现   设计模式是什么? Christopher Alexander:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心.这样你就能一次又一次地使用 ...

  9. c#小灶——使用visual studio编写第一个程序

    虽然,写程序有文本编辑器和编译器就已经足够,但是,我们为了增加工作效率还是要使用IDE. 我们接下来所有的教程都将会在visual studio中实现,visual studio简称vs,是微软开发的 ...

  10. Go组件学习——gorm四步带你搞定DB增删改查

    1.简介 ORM Object-Relationl Mapping, 它的作用是映射数据库和对象之间的关系,方便我们在实现数据库操作的时候不用去写复杂的sql语句,把对数据库的操作上升到对于对象的操作 ...