县城服务网站如何做,天津住房城乡建设网站,网络舆情的危害,软件开发项目名称有哪些一、复习
# ip地址#xff1a;一台机器在网络上的位置
# 公网ip 私网ip
# TCP协议#xff1a;可靠#xff0c;面向连接的#xff0c;耗时长#三次握手#四次挥手
# UDP协议#xff1a;不可靠#xff0c;无连接#xff0c;效率高
# ARP协议#xff1a;通过ip找mac的过程
…一、复习
# ip地址一台机器在网络上的位置
# 公网ip 私网ip
# TCP协议可靠面向连接的耗时长#三次握手#四次挥手
# UDP协议不可靠无连接效率高
# ARP协议通过ip找mac的过程
# ip协议属于网络osi中的网络层
# TCP协议和UDP协议属于传输层
# arp协议属于数据链路层
二、黏包第一条和第二条数据合并发送
tcp不会丢包会黏包
udp会丢包不会黏包
tcp黏包案例
server_tcp端
#server端
import socket
sksocket.socket()
sk.bind((127.0.0.1,8090))
sk.listen()conn,addrsk.accept()
while True:cmdinput()conn.send(cmd.encode(utf-8))ret1conn.recv(1024).decode(utf-8)ret2 conn.recv(1024).decode(utf-8)print(ret1)print(ret2)conn.close()
sk.close()
client_tcp端
#client端
import socket
import subprocess
sksocket.socket()sk.connect((127.0.0.1,8090))
while True:cmdsk.recv(1024).decode(utf-8)retsubprocess.Popen(cmd,shellTrue,stdoutsubprocess.PIPE,stderrsubprocess.PIPE)std_outstdout:(ret.stdout.read()).decode(gbk)std_errstderr:(ret.stderr.read()).decode(gbk)sk.send(std_out.encode(utf-8))sk.send(std_err.encode(utf-8))
sk.close()
运行结果 udp丢包案例udp起server和client
server_udp端 import socket
sk socket.socket(typesocket.SOCK_DGRAM)
ip_port (127.0.0.1,8090)
sk.bind(ip_port)
msg, addr sk.recvfrom(10240)
while True:cmd input()if cmd q:breaksk.sendto(cmd.encode(utf-8),addr)msg1,addr sk.recvfrom(20480)print(msg1.decode(utf-8))msg2, addr sk.recvfrom(20480)print(msg2.decode(utf-8))
sk.close()
client_udp端
import socket
import subprocess
sk socket.socket(typesocket.SOCK_DGRAM)
ip_port (127.0.0.1,8090)
sk.sendto(bhi,ip_port)
while True:cmd,addr sk.recvfrom(1024)cmd cmd.decode(utf-8)ret subprocess.Popen(cmd,shellTrue,stdoutsubprocess.PIPE,stderrsubprocess.PIPE)std_out stdout:(ret.stdout.read()).decode(gbk)std_err stderr:(ret.stderr.read()).decode(gbk)print(std_out)print(std_err)sk.sendto(std_out.encode(utf-8),addr)sk.sendto(std_err.encode(utf-8),addr)
sk.close() #不会黏包会丢包
运行结果 运行结果显示如果udp的接收数据量大小满足发送数据量大小那么就不会丢包若是不满足发送数据量大小则就会报错。而不是丢包
三 、黏包的触发
情况一发送方的缓存机制发送端要等缓冲区满才发送出去。造成黏包发送数据时间间隔很短数据很小会合并一起造成粘包
如
#server端
import socket
sksocket.socket()
sk.bind((127.0.0.1,8080))
sk.listen()
conn,addrsk.accept()
retconn.recv(12)
ret2conn.recv(12)
print(ret)
print(ret2)
conn.close()
sk.close()#关闭时会发空消息
# 多个send小的数据连在一起可能会发生黏包现象是tcp内部的优化算法引起的
#client端
import socket
sksocket.socket()
sk.connect((127.0.0.1,8080))
sk.send(bhello)
import time
# time.sleep(0.01)
sk.send(bworld)
sk.send(bdd)
sk.close()
运行结果有时会黏包有时不会 情况二接收方的缓冲机制
接收方不及时接收缓冲区的包造成多个包接收客户端发送了一段数据服务端只接收了一小部分服务器下次再接收时还是从缓冲区拿上次遗留的数据产生粘包
如
# server端
import socket
sksocket.socket()
sk.bind((127.0.0.1,8080))
sk.listen()conn,addrsk.accept()
retconn.recv(2)
ret2conn.recv(10)
print(ret)
print(ret2)
conn.close()
sk.close()# 黏包的本职问题不知道发送数据的长度
# 连续的小数据包会被合并
# client端
import socket
sksocket.socket()
sk.connect((127.0.0.1,8080))sk.send(bhello,egg)
sk.close()
运行结果 总结
黏包现象只发生在tcp协议中
1.从表面上看黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。
2.实际上主要还是因为接收方不知道消息之间的界限不知道一次性提取多少字节的数据所造成的
四、黏包的解决方案
解决方案一
问题的根源在于接收端不知道发送端将要传送的字节流的长度所以解决粘包的方法就是围绕如何让发送端在发送数据前把自己将要发送的字节流总大小让接收端知晓然后接收端来一个死循环接收完所有数据。
# server
# server 下发命令 给client
import socket
sksocket.socket()
sk.bind((127.0.0.1,8080))
sk.listen()
conn,addrsk.accept()
while True:cmdinput()if cmdq:conn.send(bq)breakconn.send(cmd.encode(gbk))numconn.recv(1024).decode(utf-8)conn.send(bok)resconn.recv(int(num)).decode(gbk)print(res)
conn.close()
sk.close()
# client
# 接收server端的命令后在自己的机器上执行
import socket
import subprocesssksocket.socket()
sk.connect((127.0.0.1,8080))
while True:cmdsk.recv(1024).decode(gbk)if cmdq:breakressubprocess.Popen(cmd,shellTrue,stdoutsubprocess.PIPE,stderrsubprocess.PIPE)std_outres.stdout.read()std_errres.stderr.read()sk.send(str((len(std_out)len(std_err))).encode(utf-8))sk.recv(1024) #oksk.send(std_out)sk.send(std_err)
sk.close()# 好处确定我到底要接收多大的数据
# recv的大小一般不超过4096
# 要在文件中配置一个配置项就是每次recv的大小 buffer4096
# 当我们发送大数据量的时候要明确的告诉接收方要发送多大数据以便接收方能准确接收到所有数据
# 多用在文件传输过程中#大文件的传输一定是按照字节读 每次读固定的字节# 传输的过程中 一边读一边传 接收端一边收一边写# send大文件之前告知大小。大小-4096-4096....--0 文件传输完# recv大文件先接受大小。再recv2048.不会丢 大小-2048-2048 --0 文件接收完
# 不好的地方多了一次交互
# 5个g数据
# send 和sendto在超过一定范围后都会报错
运行结果 存在的问题 程序的运行速度远快于网络传输速度所以在发送一段字节前先用send去发送该字节流长度这种方式会放大网络延迟带来的性能损耗
解决方案进阶
刚刚的方法问题在于我们我们在发送
我们可以借助一个模块这个模块可以把要发送的数据长度转换成固定长度的字节。这样客户端每次接收消息之前只要先接受这个固定长度字节的内容看一看接下来要接收的信息大小那么最终接受的数据只要达到这个值就停止就能刚好不多不少的接收完整的数据了。
struct模块
该模块可以把一个类型如数字转成固定长度的bytes struct.pack(i,1111111111111)struct.error: i format requires -2147483648 number 2147483647 #这个是范围 # server 下发命令 给client
import socket
import struct
sksocket.socket()
sk.bind((127.0.0.1,8080))
sk.listen()
conn,addrsk.accept()
while True:cmdinput()if cmdq:conn.send(bq)breakconn.send(cmd.encode(gbk))numconn.recv(4) #4 2048num struct.unpack(i, num)[0]resconn.recv(int(num)).decode(gbk) #2048print(res)
conn.close()
sk.close()
# 连续send两个小数据
# 两个recv第一个recv特别小
# 远程执行命令的程序ipconfig-- 2000只接收1024.就会缓存下次继续接收上次未接收完的数据#连续send两个小数据2810
# 2
# 8
#两个recv第一个recv特别小
# recv数据的长度
# 接收server端的命令后在自己的机器上执行
import socket
import subprocess
import structsksocket.socket()
sk.connect((127.0.0.1,8080))
while True:cmdsk.recv(1024).decode(gbk)if cmdq:breakressubprocess.Popen(cmd,shellTrue,stdoutsubprocess.PIPE,stderrsubprocess.PIPE)std_outres.stdout.read()std_errres.stderr.read()len_numlen(std_out)len(std_err)num_bystruct.pack(i,len_num)sk.send(num_by) # 4 2048sk.send(std_out) # 1024sk.send(std_err) #1024
sk.close()运行结果 五、ftp发送视频
sever
import socket
import struct
import json
buffer1024
# ip地址和端口号需要写在配置文件中
sksocket.socket()
sk.bind((127.0.0.1,8080))
sk.listen()conn,addrsk.accept()
# 接收
head_lenconn.recv(4) # 报头长度
head_lenstruct.unpack(i,head_len)[0]
head_jsonconn.recv(head_len).decode(utf-8)
headjson.loads(head_json)
filesizehead[filesize]
with open(head[filename],wb) as f:while filesize:print(filesize)if filesizebuffer:contentconn.recv(buffer)f.write(content)filesize-bufferelse:contentconn.recv(filesize)f.write(content)filesize0break
f.close()
conn.close()
sk.close()
#发送端
import socket
import os
import json
import struct
sksocket.socket()
sk.connect((127.0.0.1,8080))
# 发送文件
buffer2046
# 改成4096文件大小会变发送和接收时间不匹配
# 读操作快写速度慢。缓冲数据多head{filepath:rE:\test,filename: [反贪风暴3]BD国语.mp4,filesize:None}
file_pathos.path.join(head[filepath],head[filename])
filesizeos.path.getsize(file_path)
head[filesize]filesize
json_headjson.dumps(head) #字典转成了字符串
byte_headjson_head.encode(utf-8) # 字符串转成了bytes
head_lenlen(byte_head) # 报头的长度
pack_lenstruct.pack(i,head_len)
sk.send(pack_len) # 先发报头的长度
sk.send(byte_head) # 再发送bytes类型的报头
with open(file_path,rb) as f:while filesize:print(filesize)if filesizebuffer:contentf.read(buffer) # 每次读取出来的大小sk.send(content)filesize-bufferelse:contentf.read(filesize)sk.send(content)filesize0break
f.close()
sk.close()参考自https://www.cnblogs.com/Eva-J/articles/8244551.html#_label5