一、基于tcp协议的套接字(通信循环+链接循环)

服务端应该遵循:

  1.绑定一个固定的ip和port

  2.一直对外提供服务,稳定运行

  3.能够支持并发

基础版套接字:

from socket import *

server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5) conn, client_addr = server.accept() # 通信循环
while True:
data = conn.recv(1024)
conn.send(data.upper()) conn.close()
server.close()

server

from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8080)) # 通信循环
while True:
msg=input('>>: ').strip()
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data) client.close()

client

以上的程序存在两个bug

  1.当客户端单方面终止程序时,服务端抛出异常(linux可以用判断是否为空来处理)

  2.recv收到空时,一直在等待。

解决方法:

  1.异常捕获

  2.再输入时进行判断

改进版:

from socket import *

server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8082))
server.listen(5) conn, client_addr = server.accept()
print(client_addr) # 通信循环
while True:
try:
data = conn.recv(1024)
if len(data) == 0:break # 针对linux系统
print('-->收到客户端的消息: ',data)
conn.send(data.upper())
except ConnectionResetError:
break conn.close()
server.close()

server

from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8081)) # 通信循环
while True:
msg=input('>>: ').strip() #msg=''
if len(msg) == 0:continue
client.send(msg.encode('utf-8')) #client.send(b'')
# print('has send')
data=client.recv(1024)
# print('has recv')
print(data) client.close()

client

链接循环:服务器改进

from socket import *

server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8082))
server.listen(5) conn, client_addr = server.accept()
print(client_addr) # 通信循环
while True:
try:
data = conn.recv(1024)
if len(data) == 0:break # 针对linux系统
print('-->收到客户端的消息: ',data)
conn.send(data.upper())
except ConnectionResetError:
break conn.close()
server.close()

server

模拟ssh实现远程执行命令

from socket import *
import subprocess server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8081))
server.listen(5) # 链接循环
while True:
conn, client_addr = server.accept()
print(client_addr) # 通信循环
while True:
try:
cmd = conn.recv(1024) #cmd=b'dir'
if len(cmd) == 0: break # 针对linux系统
obj=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
print(len(stdout) + len(stderr))
conn.send(stdout+stderr)
except ConnectionResetError:
break conn.close() server.close()

server

from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8081)) # 通信循环
while True:
cmd=input('>>: ').strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))
cmd_res=client.recv(1024000)
print(cmd_res.decode('gbk')) client.close()

client

recv其实是和本地计算机要数据,所以解码的时候应该用gbk格式

二、粘包

注:只有tcp存在粘包现象,udp永远不存在粘包。

tcp是面向流的协议,发送端可以1k,1k的发送数据,而接收端可以2k,2k的提取数据,发送方往往收集到足够多的数据后才生成一个tcp段,若连续几次需要发送的数据都很少,通常tcp会根据(Nagle)优化算法,把这些数据合成一个TCP段后发出去,这样接收方就收到了粘包数据。

总的来说,粘包问题就是因为接收方不知道消息之间的界限,不知道一次性提取多少字节造成的。

解决方法:(服务端)为字节流加上一个报头,将这个报头(字典形式)json序列化,编码。然后用struct发送报头的长度,再发送报头,最后发送真实数据

     (客户端)先解出报头的长度(struct),再接收报头,将拿到的报头解码再反序列化,得到报头字典,最后接收真正的数据

# 服务端必须满足至少三点:
# 1. 绑定一个固定的ip和port
# 2. 一直对外提供服务,稳定运行
# 3. 能够支持并发
from socket import *
import subprocess
import struct
import json server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8081))
server.listen(5) # 链接循环
while True:
conn, client_addr = server.accept()
print(client_addr) # 通信循环
while True:
try:
cmd = conn.recv(1024) # cmd=b'dir'
if len(cmd) == 0: break # 针对linux系统
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
# 1. 先制作报头
header_dic = {
'filename': 'a.txt',
'md5': 'asdfasdf123123x1',
'total_size': len(stdout) + len(stderr)
}
header_json = json.dumps(header_dic)
header_bytes = header_json.encode('utf-8') # 2. 先发送4个bytes(包含报头的长度)
conn.send(struct.pack('i', len(header_bytes)))
# 3 再发送报头
conn.send(header_bytes) # 4. 最后发送真实的数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break conn.close() server.close()

server

from socket import *
import struct
import json client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8081)) # 通信循环
while True:
cmd=input('>>: ').strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))
#1. 先收4bytes,解出报头的长度
header_size=struct.unpack('i',client.recv(4))[0] #2. 再接收报头,拿到header_dic
header_bytes=client.recv(header_size)
header_json=header_bytes.decode('utf-8')
header_dic=json.loads(header_json)
print(header_dic)
total_size=header_dic['total_size'] #3. 接收真正的数据
cmd_res=b''
recv_size=0
while recv_size < total_size:
data=client.recv(1024)
recv_size+=len(data)
cmd_res+=data print(cmd_res.decode('gbk')) client.close()

client

补充:struct模块

该模块可以帮一个类型,如数字,转成固定长度的bytes

1、 struct.pack
struct.pack用于将Python的值根据格式符,转换为字符串(因为Python中没有字节(Byte)类型,可以把这里的字符串理解为字节流,或字节数组)。其函数原型为:struct.pack(fmt, v1, v2, ...),参数fmt是格式字符串,关于格式字符串的相关信息在下面有所介绍。v1, v2, ...表示要转换的python值。

2、 struct.unpack
struct.unpack做的工作刚好与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数返回一个元组。

import struct

obj1=struct.pack('i',13321111)
print(obj1,len(obj1))#b'\x97C\xcb\x00' 4 res1=struct.unpack('i',obj1)
print(res1[0])#

python-基于tcp协议的套接字(加强版)及粘包问题的更多相关文章

  1. python 之 网络编程(基于TCP协议的套接字通信操作)

    第八章网络编程 8.1 基于TCP协议的套接字通信 服务端套接字函数 s.bind() 绑定(主机,端口号)到套接字 s.listen() 开始TCP监听 s.accept() 被动接受TCP客户的连 ...

  2. 网络编程----socket介绍、基于tcp协议的套接字实现、基于udp协议的套接字实现

    一.客户端/服务器架构(C/S架构)                                                即C/S架构,包括: 1.硬件C/S架构(打印机) 2.软件C/S架 ...

  3. 网络编程(二)--TCP协议、基于tcp协议的套接字socket

    一.TCP协议(Transmission Control Protocol 传输控制协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会 ...

  4. 网络编程之TCP三次握手与四次挥手、基于TCP协议的套接字编程

    目录 TCP三次握手和四次挥手 背景描述 常用的熟知端口号 TCP概述 TCP连接的建立(三次握手) TCP四次挥手 如果已建立连接,客户端突然断开,会怎么办呢? 基于TCP协议的套接字编程 什么是S ...

  5. 什么是 socket?简述基于 tcp 协议的套接字通信流程?

    Socket的英文原义是"孔"或"插座".通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄, 可以用来实现不同虚拟机或不同计 ...

  6. 网络编程(二)——TCP协议、基于tcp协议的套接字socket

    TCP协议与基于tcp协议的套接字socket 一.TCP协议(流式协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的 ...

  7. [网络编程之Socket套接字介绍,套接字工作流程,基于TCP协议的套接字程序]

    [网络编程之Socket套接字介绍,套接字工作流程,基于TCP协议的套接字程序] 为何学习socket套接字一定要先学习互联网协议: 1.首先:要想开发一款自己的C/S架构软件,就必须掌握socket ...

  8. 01:osi七层---基于TCP协议的套接字(socket)

    1 : osi 七层,tcp/ip 五层 1 cs架构和bs架构2 互联网3 osi七层 tcp/ip五层 -物理层   -网线.光纤        -数据链路层       -网卡        - ...

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

    06.26自我总结 1.关于Socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在 ...

  10. 网络编程[第一篇]基于tcp协议的套接字编程

    将服务端-客户端的连接比作双方打电话的过程 2019-07-24 一.客户端 主动的一方: 客户端实例化一个socket对象--> 主动像服务端发送连接请求--> (服务端接受请求后即可进 ...

随机推荐

  1. Python3 视频教程,全网最全的视频教程,爬虫,从入门到实战

    需要联系我:QQ:1844912514 最新Python基础班+就业班视频教程 链接: python分布式爬虫打造搜索引擎链接: https://pan.baidu.com/s/1N7HL7U0gQX ...

  2. CountDownLatch类实现同步

    首先我们看一个普通的多线程代码 class MyThread implements Runnable { @Override public void run() { System.out.printl ...

  3. java &与&& |与||的区别

    一.与操作和或操作的区别 (1)在Java程序中,使用与操作,要求所有表达式的判断结果都是TRUE,才为真,若有一个为FALSE,那么最终判断结果则为FALSE (2)使用或操作,只要其中有一个表达式 ...

  4. Java PDF转图片

    maven依赖: <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbo ...

  5. QProcess与外部程序的调用

    项目做到一定阶段,常常须要在原来的project上调用外部程序. Qt为此提供了QProcess类,QProcess可用于完毕启动外部程序,并与之交互通信. 一.启动外部程序的两种方式:(1)一体式: ...

  6. 浅谈vuex使用方法(vuex简单实用方法)

    Vuex 是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.Vuex 也集成到 Vu ...

  7. 使用Portainer管理Docker

    Portainer Portainer安装非常简单,只需不到一分钟.Portainer完全支持Docker 1.10及更高版本. Mac 快速安装运行: 命令行输入: docker pull port ...

  8. 自定义的AdBlock过滤规则

    自定义的AdBlock过滤规则 # 屏蔽百度首页的广告流 www.baidu.com##DIV[id="u1"] www.baidu.com##DIV[id="qrcod ...

  9. CentOS7防火墙问题

    CentOS6关闭防火墙使用以下命令, //临时关闭service iptables stop//禁止开机启动chkconfig iptables off CentOS7中若使用同样的命令会报错, s ...

  10. ELK-filebeat收集日志到Kafka,并转存ES

    https://blog.51cto.com/tryingstuff/2052271 场景需求 在有些不需要安装java环境的服务器如Nginx,php等为主的web 应用可以使用filebeat来对 ...