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转换
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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)