Python学习笔记--------UDP和TCP协议

UPD和TCP协议

一、UPD

UDP的服务端和客户端时通过“发出请求”和“接受请求”区分的
UDP的服务端是要绑定端口和地址,目的是让其他客户端能够正确的发送到进程

客户端代码
from socket import *
#创建数据套接字,SOCK_DGRAM是UDP协议(无连接), SOCK_STREAM是TCP协议(有连接),AF_INET面向网络
client_socket=socket(AF_INET,SOCK_DGRAM)
#定义服务器端的地址和端口
post=(地址,端口)
#套接字绑定客户端
while True:
#将字符串改为字节形式进行传输
data=input("请输入:").encode('utf-8')
#发送的信息
client_socket.sendto(data,post)
print("发送完成")
#解码
if data.decode('utf-8')=='exit':
break
client_socket.close()```
服务端代码
from socket import *
#创建套接字
server_socket=socket(AF_INET,SOCK_DGRAM)
#定义服务器端的地址和端口
post=("地址",端口)
#套接字绑定服务器
server_socket.bind(post)
while True:
data=server_socket.recvfrom(1024)
print(data[0].decode('utf-8'))
#注解:recvfrom是由(发送信息,(客服端ip地址和端口))组成

二、TFTP协议

TFTP的作用是客户端和服务端传输简单文件的协议,端口号是69

1、原理

客户端进行需求文件的输入并以数据包(pack)的形式进行加压(转换为字节流)。将数据包传给服务端,服务端进行解压(unpack),进行数据包的解读,查找是否存在客户所需文件并创建一个新的socket将文件传给客户端,进行判断。若不存在所需文件,给客户端一个error的数据包告诉客户下载失败;若存在所需文件,对文件进行读取(512个字节为一个数据包),传给客户,客户解压数据包,并确定一个ack数据包进行回馈服务端,服务端解读,循环往复,直至文件读取结束。

2、TFTP数据包类型

如下:

照片

3、python的struct转换

201112171613443928

4、客户端代码
from socket import *
import struct
#基于网络AF_INEF和无连接的套接字
#客户端器的socket
s=socket(AF_INET,SOCK_DGRAM)
host_port=('地址','端口')
#
file_name=input("请输入需要传输的文件:")
#将需要输入的数据变为数据包
data=struct.pack('!H%dsb5sb'%len(file_name),1,file_name.encode('utf-8'),0, 'otect'.encode('utf-8'),0)
#将带有地址和端口的数据包发送给服务端
s.sendto(data,host_port)
#客户端创建空白文件
f=open('client_'+file_name,'ab')
while True:
#客户端接受服务端的数据,数据有两种,下载文件的数据包和error的数据包
recv_data,(server_ip,server_port)=s.recvfrom(1024)
operator_code,num=struct.unpack('!hh',recv_data[:4])
if int(operator_code)==5:
print("你下载的文件不存在")
break
f.write(recv_data[4:])
if len(recv_data)<516 :
print('客户端下载成功')
break
#客户端收到数据 发送一个ack的应答
ack_package=struct.pack('!HH',4,num)
s.sendto(ack_package,(server_ip,server_port))
f.close()
s.close()
5、服务端代码
import struct
from socket import *
#服务器的socket,基于网络AF_INEF和无连接的套接字
s=socket(AF_INET,SOCK_DGRAM)
s.bind(('',89))#空的ip默认所有的ip
def download(file_name,client_ip,client_port):
#创建一个新的socket,负责将客户端的数据传给客户
server_sock=socket(AF_INET,SOCK_DGRAM)
#定义需要下载的数目
print(f'{file_name}:{type(file_name)}')
num=1
flag=True
try:
#读取文件
f=open(file_name,'rb')
except:
#如果下载的文件名不存在,则给客户端一个错误的数据包提示
error_package=struct.pack('!HH5sb',5,num,'error'.encode('utf-8'),0)
server_sock.sendto(error_package,(client_ip,client_port))
flag=False
print('flag:',flag)
while flag:
rece_data=f.read(512)
#创建一下数据包
data_package=struct.pack('!hh',3,num)+rece_data
server_sock.sendto(data_package,(client_ip,client_port))
if len(rece_data) < 512 :
print("客户端:%s的文件,下载完成"%client_ip)
break #退出进程,结束
#服务端接受客户端的确定是数据
reve_ack=server_sock.recvfrom(1024)
operate_code,ack_num=struct.unpack('!HH',reve_ack[0])
print("客户端%s,的确定信息"%client_ip,ack_num)
num+=1
print(file_name)
if int(operate_code)!=4 or int(operate_code)<1:
break
server_sock.close()
def server():
#服务器将客户端的数据传入过来,并等待接收数据
while True:
recv_data,(client_ip,client_port)=s.recvfrom(1024)
#将数据包进行解压,unpack返回值是元组
if struct.unpack('!b5sb',recv_data[-7:])==(0,b'otect',0):
#解压数据包
operate_code=struct.unpack('!H',recv_data[:2])[0]
file_name=recv_data[2:-7].decode('utf-8')
#操作码是1表示下载文件,操作码是2表示上传文件
print(operate_code,file_name)
if int(operate_code)==1:
print("客户端下载文件:%s"%file_name)
#进行下载操作,传文件名,客户端ip和端口
download(file_name,client_ip,client_port)
if __name__ == '__main__':
server()

三、TCP协议

TCP具有三次握手协议

1、TCP服务端的功能流程

1、socket创建一个套接字
2、bind绑定id和port
3、listen() 开始TCP监听
4、accept() 使套接字变为可以被动连接,,等待客户端的连接
5、recv/send接收发送数据

2、模拟qq聊天客户端代码
from socket import *
client_socket=socket(AF_INET,SOCK_STREAM)
client_port=('',8080)
#客户端来连接ip地址和端口
client_socket.connect(client_port)
while True:
data=input('send:')
if len(data)>0:
client_socket.send(data.encode('utf-8'))
if data=='exit':
break
server_data=client_socket.recv(1024)
print('服务端:',server_data.decode('utf-8'))
client_socket.close()
3、模拟qq聊天服务端代码
from socket import *
server_socket=socket(AF_INET,SOCK_STREAM)
post=('',8080)
#绑定端口,变为服务器
server_socket.bind(post)
server_socket.listen(1)
while True:
new_socket,client_addr=server_socket.accept()
while True:
data=new_socket.recv(1024)
if len(data)>0:
print('客户端:',data.decode('utf-8'))
if (data.decode('utf-8')=='exit'):
print("客户端已经退出!")
break
server_data=input('send:')
new_socket.send(server_data.encode('utf-8'))
new_socket.close()
server_socket.close()

四、粘包问题

1、粘包的原因

在UDP协议中,UDP是面向信息的,基于数据包的形式进行发送数据,每个数据包之间都会有明显的分界,并不会导致数据的粘包。而在TCP协议中,TCP是面向连接,基于流的形式发送数据,会将数据存放在缓存区再进行发送,在数据缓存在缓存区的时候,就会产生粘包。

2、粘包的情况

1、发送端发送的单个数据包过小,缓存区可以存放很多数据包,则会产生粘包
2、发送端发送的单个数据包过大,缓存区只能缓存数据包的部分,则会产生粘包3、在TCP协议中,为了提高效率,节省时间,增加了优化机制,在两个数据包发送的间隔时间很短时,会将两个数据包进行合并,则会产生粘包

3、粘包的解决

在文件发送前,先向服务端发送文件的字节长度,然后接收端再用一个死循环接受所有的数据。用struct将序列化后的数据长度打包为4个字节。此后,判断字节的长度便于区分每个数据包。

客户端代码

from socket import *
import struct
#创建一个服务端socket
server_socket=socket(AF_INET,SOCK_STREAM)
port=('',8080)
server_socket.bind(port)
#TCP监听
server_socket.listen(3)
f=open(r'D:\123.txt','wb')
conn,client_ip=server_socket.accept()
data_size=conn.recv(4)
size=struct.unpack('!i',data_size)[0]
rece_data=0
while rece_data<size:
data=conn.recv(1014)
rece_data+=len(data)
f.write(data)
print("客户所需文件已经下载完成")
f.close()
conn.close()
server_socket.close()

服务端代码

from socket import *
server_socket=socket(AF_INET,SOCK_STREAM)
post=('',8080)
#绑定端口,变为服务器
server_socket.bind(post)
server_socket.listen(1)
while True:
new_socket,client_addr=server_socket.accept()
while True:
data=new_socket.recv(1024)
if len(data)>0:
print('客户端:',data.decode('utf-8'))
if (data.decode('utf-8')=='exit'):
print("客户端已经退出!")
break
server_data=input('send:')
new_socket.send(server_data.encode('utf-8'))
new_socket.close()
server_socket.close()

本文作者:灰之魔女伊蕾娜

本文链接:https://www.cnblogs.com/daohengdao/p/15999350.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   灰之魔女伊蕾娜  阅读(93)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开