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. 【git】Git的使用

    一.安装git 1.windows下安装一个Git 2.lInux下yum(apt-get) install git 二.使用git连接github 使用git连接github时,需要将linux下产 ...

  2. windows上node开发注意事项

    windows上进行node.react开发的必要步骤: 1.使用nvm进行node及npm包管理工具,记得使用npm config set ...:2.另外react仅支持python3.0以下的版 ...

  3. 【Demo 1】基于object_detection API的行人检测 1:环境与依赖

    环境 系统环境: win10.python3.6.tensorflow1.14.0.OpenCV3.8 IDE: Pycharm 2019.1.3.JupyterNotebook 依赖 安装objec ...

  4. JAVA遍历机制的性能的比较

        本文首发于cartoon的博客     转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/java/java%E9%81%8D%E5% ...

  5. Angular JS 中 ng-controller 值复制和引用复制

    我们知道在使用ng-app或者ng-controller指令的时候,都会创建一个新的作用域($rootScope或者是$scope),并且在使用ng-controller指令创建的作用域会继承父级作用 ...

  6. CSS3☞transform变换

    transform CSStransform属性允许你旋转,缩放,倾斜或平移给定元素.这是通过修改CSS视觉格式化模型的坐标空间来实现的. DEMO /* Keyword values */ tran ...

  7. 【Android】Jetpack中的ViewModel:自动保存页面数据

    目录 ViewModel 简介 ViewModel的使用方法 ViewModel 简介   ViewModel 允许数据在配置更改(如屏幕旋转)后仍然存在,使用 ViewModel 可以免去开发者花费 ...

  8. Codeforces Round #192 (Div. 2) (330B) B.Road Construction

    题意: 要在N个城市之间修建道路,使得任意两个城市都可以到达,而且不超过两条路,还有,有些城市之间是不能修建道路的. 思路: 要将N个城市全部相连,刚开始以为是最小生成树的问题,其实就是一道简单的题目 ...

  9. Kubernetes容器集群管理环境 - Prometheus监控篇

    一.Prometheus介绍之前已经详细介绍了Kubernetes集群部署篇,今天这里重点说下Kubernetes监控方案-Prometheus+Grafana.Prometheus(普罗米修斯)是一 ...

  10. 数据结构之堆栈java版

    import java.lang.reflect.Array; /* 具体原理在c++版已经说的很清楚,这里不再赘述, 就提一点:java的泛型具有边界效应,一旦离开作用域立马被替换为object类型 ...