一、背景说明

昨天一个同事让帮忙写个服务,用于接收并返回他那边提交过来的数据,以便其查看提交的数据及格式是否正确。

开始想用django写个接口,但写接口接口名称就得是定死的,他那边只能向这接口提交数据;接收一下就返回这种事情不如直接写个socket监听然后返回去。

以前也没怎么写正经的socket编程,基本是能收发点数据应差不多了,此次收发的数据一多就出了问题。

一是没接收完客户端要发送的数据就给客户端回RST,二是没发送完要给客户端发送的数据就又直接给客户端发送RST。

二、问题处理

2.1 tcp收发处理

使用s.recv()接收数据时,s.recv()只管从操作系统缓冲区中读取数据,阻塞模式下只要读到数据、非阻塞模式下不管读到读不到或者读到多少,函数都算执行完了程序会继续往后执行。因此接收大量数据时我们需要不断使用s.recv()进行读取然后设定一个终止标志。

使用s.send()发送数据时,s.send()只管通知操作系统发送数据,操作系统每次只是尽力发送数据然后把本次发送的数据作为返回值并不保证数据发送完成。因此在发送大量数据时我们需要不断使用s.send()发送然后设定一个终止标志。(不过如果单纯说python那可以使用snedall()函数来实现一次发送完,sendall()的实现方法和我们这里说的意思一样)

另外注意如果测试自己使用requests等作为客户端时,服务端的返回要加上http的响应头部,不然数据原样返回requests等进行解析会因不是一个正确的http响应而出错。

2.2 有问题程序

import socket

# 获取本地主机名
host = socket.gethostname()
port = 9999 # 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定端口号
server_socket.bind((host, port))
# 设置最大连接数,超过后排队
server_socket.listen(5) # 这里实现的是接收客户端发来的数据、打印、然后再原样返回给客户端
while True:
client_socket, addr = server_socket.accept()
print(f"连接地址: {str(addr)}") # 错误二、当发来的数据很长时tcp不会等接收完成再执行下一条语句,这里没处理这个问题
result = client_socket.recv(1024 * 1024)
# 问题一、decode默认使用utf-8编码,但当发来的数据有utf-8不可解码内容时会报异常,这里没捕获异常
print(f"{result.decode()}") # 错误三、发送时tcp不会等发送完再执行下一条语句,这里没处理这个问题
client_socket.send(result)
# 注意四、如果客户端中的接收代码是和上边错误二一样的,那么没发完也会被客户端reset
client_socket.close()

2.3 修正后程序

import socket

# 获取本地主机名
host = socket.gethostname()
port = 9999 # 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定端口号
server_socket.bind((host, port))
# 设置最大连接数,超过后排队
server_socket.listen(5)
print("服务器程序已启动") while True:
# 建立客户端连接
client_socket, addr = server_socket.accept()
# 阻塞模式,设置接收超时时长为1秒,1秒内没有新数据视为数据已传送完成
client_socket.settimeout(1)
# client_socket.setblocking(0)
print(f"连接地址: {str(addr)}")
# 这里result不能赋值为None,否则下边的result += tmp_result会因类型不一致报错
# 这里result不能赋值为字符串"",否则下边的result += tmp_result会因类型不一致报错
result = b""
tmp_result = "" # 错误二修正:此处while循环用于确保接收完所有数据再执行后续指令
while True:
# 每次最多读取2048字节
try:
tmp_result = client_socket.recv(2048)
# 1秒内无数据,触发超时异常,此时我们判定为数据已接收完成break退出
# 不能使用获取数据为空作为退出标志,因为阻塞模式除非是已建立的网络连接被拆除不然读不到数据是不会返回的
except socket.timeout as e:
print(f"{e}")
break
# 将本次读取到的内容拼接到result中
result += tmp_result # 问题一修正:对解码异常进行捕获,直接以byte形式输出
try:
print(f"{result.decode()}")
except:
print(f"{result}") total_lenght = result.__len__()
print(f"\r\ntotal_length :{total_lenght}")
flag = 0
# 错误三修正:确保数据发送完才执行后续代码
# python其实可以使用sendall()来完成,但sendall本身也是类似以下形式,为了通用性我们这里暂时不用
while True:
# 每次从已发送数据位置发送
# 每次返回的是本次发送数据长度
tmp_flag = client_socket.send(result[flag:])
flag += tmp_flag
# 如果已发送完则退出
if flag == total_lenght:
break # 至于问题四,客户端未完成接收即返回reset,那就只能由客户端去处理了
client_socket.close()

参考:

https://stackoverflow.com/questions/34252273/what-is-the-difference-between-socket-send-and-socket-sendall

Python3 Tcp未发送/接收完数据即被RST处理办法的更多相关文章

  1. 怎样保证socket.recv接收完数据

    最近在使用python进行网络编程开发一个通用的tcpclient测试小工具.在使用socket进行网络编程中,如何判定对端发送一条报文是否接收完成,是进行socket网络开发必须要考虑的一个问题.这 ...

  2. TCP通信实现对接硬件发送与接收十六进制数据 & int与byte的转换原理 & java中正负数的表示

    今天收到的一份需求任务是对接硬件,TCP通信,并给出通信端口与数据包格式,如下: 1.首先编写了一个简单的十六进制转byte[]数组与byte[]转换16进制字符串的两个方法,如下: /** * 将十 ...

  3. android TCP 客户端(仅接收数据)

    配合log4net使用,用来接收调试信息.因此,此客户端只管通过TCP接收字符串数据,然后显示在界面上. 接收TCP数据 try { Socket s = new Socket("192.1 ...

  4. tcp接收xml数据解析

    避免tcp接收xml数据时加上xml数据长度,根据xml数据特点来解析recv到的xml数据 int nPos1 = 0; int nPos2 = 0; int nTempPos = 0; int n ...

  5. python3 Serial 串口助手的接收读取数据

    其实网上已经有许多python语言书写的串口,但大部分都是python2写的,没有找到一个合适的python编写的串口助手,只能自己来写一个串口助手,由于我只需要串口能够接收读取数据就可以了,故而这个 ...

  6. 网络编程--使用TCP协议发送接收数据

    package com.zhangxueliang.tcp; import java.io.IOException; import java.io.OutputStream; import java. ...

  7. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(二十九):推送avro格式数据到topic,并使用spark structured streaming接收topic解析avro数据

    推送avro格式数据到topic 源代码:https://github.com/Neuw84/structured-streaming-avro-demo/blob/master/src/main/j ...

  8. 【转】使用TCP协议连续传输大量数据时,是否会丢包,应如何避免?

    使用TCP协议连续传输大量数据时,是否会丢包,应如何避免? 比如发送文件.记得有人提过可能会发生什么堆栈溢出.怎样避免呢?是不是可以收到数据后发送确认包,收到确认包后再继续发送.或是发送方发送了一些数 ...

  9. TCP协议(包括TCP的连接过程,数据分段,TCP有关服务器优化)

    Transmission Control Protocol/Internet Protocol 传输控制协议/因特网互联协议 TCP/IP是一个Protocol Stack(协议栈),包括TCP.IP ...

随机推荐

  1. Deep Convolution Auto-encoder

    一.概念介绍 自编码器是一种执行数据压缩的网络架构,其中的压缩和解压缩功能是从数据本身学习得到的,而非人为手工设计的.自编码器的两个核心部分是编码器和解码器,它将输入数据压缩到一个潜在表示空间里面,然 ...

  2. bootstrap 无需引入 直接使用

    <link href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="styl ...

  3. IP通信基础学习第九周

    H3C单臂路由: 交换机的所有接口是在同一个广播域 用vlan进行隔离广播域 创建vlan,display可查看是否创建成功 进入接口是Interface,配置接口Port 先测试相同的vlan ,可 ...

  4. redis参数说明

    感谢网友分享. 1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程 daemonize no 2. 当Redis以守护进程方式运行时,Redis默认会把pid写 ...

  5. 【题解】Luogu P4438 [HNOI/AHOI2018]道路

    原题传送门 实际就是一道简单的树形dp 设f[u][i][j]表示从根结点到结点u经过i条未翻修公路,j条未翻修铁路的贡献最小值 边界条件:f[leaf][i][j]=(A+i)(B+j)C (题目上 ...

  6. Q语言-[帝王三国送将辅助]

    纯属自己写的, 玩同一个游戏的朋友,需要送将的, 把需要送的将改名为送, 然后启动辅助即可 本辅助只支持1024x576 191dpi 附上源码 //本源码初始化分辨率1024x576[夏天] Dim ...

  7. Django多表查询

    一.前言 1.什么是ORM? ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候, ...

  8. NOIP 2018 划水记

    (此处不应有目录) (本来想咕掉这篇游记) Day -1 今天信心题,这个毒瘤出题人怎么出了一堆垃圾题(smallfat批判这个垃圾题). T2,T3是送分题.T1考了个noip根本不会考得类欧几里德 ...

  9. spring boot 2使用Mybatis多表关联查询

    模拟业务关系:一个用户user有对应的一个公司company,每个用户有多个账户account. spring boot 2的环境搭建见上文:spring boot 2整合mybatis 一.mysq ...

  10. CentOS7攻克日记(二) —— 配置初始化及网络配置

    reboot之后到达这个页面,点击第一个进去同意一下协议之后Done,然后点击右下角的FINISH