一、UDP套接字

服务端

  # udp是无链接的,先启动哪一端都不会报错
# udp没有链接,与tcp相比没有链接循环,只有通讯循环
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #创建一个服务器的套接字
server.bind() #绑定服务器套接字
inf_loop: #服务器无限循环
cs = server.recvfrom()/server.sendto() # 对话(接收与发送)
server.close() # 关闭服务器套接字

  

客户端

  
  client = socket()   # 创建客户套接字
comm_loop: # 通讯循环
client.sendto()/client.recvfrom() # 对话(发送/接收)
client.close() # 关闭客户套接字

  

简单例子

服务端

  import socket

ip_port = ('127.0.0.1',8081)
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(ip_port)
while True:
print('udp服务端开始运行了')
data,addr = server.recvfrom(1024)
print(data.decode('utf-8'))
msg = input("请输入").strip()
server.sendto(msg.encode("utf-8"),addr)

  

客户端

  
  import socket

ip_port = ('127.0.0.1', 8081)
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
print('udp客户端开始运行了')
msg = input("请输入").strip()
server.sendto(msg.encode("utf-8"), ip_port)
data, addr = server.recvfrom(1024)
print(data.decode("utf-8"))

  

注意:udp 可以发空 数据报协议 说是发空,其实不是空 ,还有一个IP 端口的信息,发空时 带个端口信息,

tcp:不是一一对应的,udp:是一一对应的 数据报完整的

用upd做一个ntp时间服务器

服务端

  
  import socket
import time

ip_port = ("127.0.0.1",8080)
buffer_size = 1024
ntp_server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
ntp_server.bind(ip_port)

while True:
data,addr = ntp_server.recvfrom(buffer_size)
print("收到客户端的命令是",data.decode("utf-8"))
if not data:
fmt = "%Y-%m-%d %X"
else:
fmt = data.decode("utf-8")
time_now = time.strftime(fmt,time.localtime())
ntp_server.sendto(time_now.encode("utf-8"),addr)

  

客户端

  
  import socket

ip_port = ("127.0.0.1",8080)
buffer_size = 1024
ntp_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:
msg = input(">>>")
ntp_client.sendto(msg.encode("utf-8"),ip_port)
recv_msg,addr = ntp_client.recvfrom(buffer_size)
print(recv_msg.decode("utf-8"))

  

基于udp简单实现QQ聊天

服务端

  from socket import *
udp_server= socket(AF_INET,SOCK_DGRAM)
udp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
udp_server.bind(('127.0.0.1',8080))
print('start running...')

while True:
qq_msg,addr = udp_server.recvfrom(1024)
print('来自[%s:%s]的一条消息:\033[44m%s\033[0m'%(addr[0],addr[1],qq_msg.decode('utf-8')))
back_msg = input('回复消息:>>').strip()
udp_server.sendto(back_msg.encode('utf-8'),addr)
udp_server.close()

  

客户端

  
  from socket import *
udp_client = socket(AF_INET,SOCK_DGRAM)
qq_name_dic = {
'pony':('127.0.0.1',8080),
'jack':('127.0.0.1',8080),
'charles':('127.0.0.1',8080),
'nick':('127.0.0.1',8080)
}
while True:
print("QQ名单列表:")
for i in qq_name_dic.keys():
print(i)
qq_name = input('请输入聊天对象:>>').strip()
if qq_name not in qq_name_dic: continue
while True:
msg = input('请输入消息,回车发送:').strip()
if msg=='quit':break
if not msg or not qq_name or qq_name not in qq_name_dic:continue
udp_client.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
back_msg,addr = udp_client.recvfrom(1024)
print('来自[%s:%s]的一条消息:\033[41m%s\033[0m'%(addr[0],addr[1],back_msg.decode('utf-8')))
udp_client.close()

  

二、tcp与udp对比

tcp基于链接通信,数据流式协议

  • 基于链接,则需要listen(backlog),指定连接池的大小

  • 基于链接,必须先运行的服务端,然后客户端发起链接请求

  • 对于mac/linux系统:如果客户端断开了链接,那服务端的链接recv将会阻塞,通讯循环收到的是一直空(解决方法是:服务端在收消息后加上if判断,空消息就break掉通信循环)

  • 对于windows系统:如果一端断开了链接,那另外一端的链接也跟着出错,(解决方法是:服务端通信循环内加异常处理,捕捉到异常后就break掉通讯循环)

  • 相对于upd传输速度慢

  • 流式协议 会粘包 不可以发空 send recv 不是 一 一对应

  • tcp适用于:

    • 数据一定要可靠

    • 远程执行命令

    • 下载文件

udp无链接,数据报式协议

  • 无链接,因而无需listen(backlog),更加没有什么链接池

  • 无链接,udp的sendto不用管是否有一个正在运行的服务端可以一直发消息,只不过数据可能会丢失

  • recvfrom收的数据小于sendto发送的数据时,在mac和linux系统上数据直接丢失,在windows系统上发送的比接收的大直接报错

  • 只有sendto发送数据没有recvfrom收数据,数据丢失

  • 数据报协议 不会粘包 可以发空 sendto recvfrom 一 一 对应 数据报协议 数据不安全 有可能发送数据 > 1024 或者网络网络异常 数据没了

  • udp适用于

    • QQ

    • 查询操作 eg: ntp时间服务器 dns服务器(查域名,转ip) 能保证查询效率高,数据虽然不可靠,传输过程中可能会发生数据丢失

三、基于socket实现文件网络传输

简单版本

服务端

  
  import socket
import os
import hashlib
import json
import struct

ip_port = ("127.0.0.1",9001)
back_log = 5
buffer_size = 1024
base_path = os.path.dirname(os.path.abspath(__file__))
share_path =os.path.join(base_path,"share")

ftp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ftp_server.bind(ip_port)
ftp_server.listen(back_log)

def creat_md5(file):
md5_value = hashlib.md5()
with open(file,"rb") as f:
while True:
data = f.read(1024)
if not data:
break
md5_value.update(data)
return md5_value.hexdigest()


while True:
print("FTP服务器开始运行啦!")
conn,address = ftp_server.accept()
while True:
try:
# 第一步:收命令
res = conn.recv(8096) #"get a.txt"
if not res: continue
# 第二步:解析命令, 提取相应的命令参数
cmds = res.decode("utf-8").split()
file_name = cmds[1]
if cmds[0] == "get":

file_path = os.path.join(share_path,file_name)
file_md5 = creat_md5(file_path)
file_size = os.path.getsize(file_path)
#第三步:以读的方式打开文件,读取文件内容 发送给客户端,
# 1、先自制报头,传递文件的相关信息
header_dic = {
"filename":file_name,
"filemd5":file_md5,
"filesize":file_size
}
header_json = json.dumps(header_dic).encode("utf-8")
header_length = len(header_json)
header_struct = struct.pack("i",header_length)
# 2、发送报头的长度
conn.send(header_struct)
# 3、发送报头,传递文件的各种信息
conn.send(header_json)
# 4、打开文件,读取内容,一行一行的发送读取的内容给客户端
with open(file_path,"rb") as f:
for line in f:
conn.send(line)

except Exception as e:
print(e)
break
conn.close()

  

客户端

  
  import socket
import os
import struct
import json
import time

ip_port = ("127.0.0.1", 9001)
buffer_size = 1024
base_path = os.path.dirname(os.path.abspath(__file__))
download_path = os.path.join(base_path,"download")

ftp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ftp_client.connect(ip_port)

while True:
# 第一步:写命令,发送命令给服务端
cmd = input("请输入命令: ")
if not cmd: continue
if cmd == "quit": break
ftp_client.send(cmd.encode("utf-8"))
# 第二步:收取自制报头的长度
header_struct = ftp_client.recv(4)
header_length = struct.unpack("i", header_struct)[0]
print("报头长度",header_length)
# 第三步:收取自制报头的信息
header_json = ftp_client.recv(header_length).decode("utf-8")
header_dic = json.loads(header_json)
print("报头字典",header_dic)
# 第四步:根据报头信息拼出文件的各种信息
file_name = header_dic["filename"]
file_md5 = header_dic["filemd5"]
file_size = header_dic["filesize"]
file_download_path = os.path.join(download_path,file_name)
# 第五步:以写的方式打开一个新文件,接收服务端发来的文件内容写入客户的新文件
with open(file_download_path,"wb") as f:
data_size = 0
start_time = time.perf_counter()
while data_size < file_size:
line = ftp_client.recv(buffer_size)
f.write(line)
data_size = data_size + len(line)
# print("已经写入数据",data_size)
download_percent = int((data_size/file_size)*100)
# print("百分比",download_percent)
a = "*" * download_percent
# print(a)
b = "." * (100 - download_percent)
# print(b)
c = (data_size/file_size)*100
during_time = time.perf_counter() - start_time
print("\r{:3.0f}%[{}-{}]共计用时:{:.3f}s".format(c,a,b,during_time),end="")
# sys.stdout.flush()
print("\n" + "执行结束")
ftp_client.close()

  

基于类写的文件传输

服务端

  
  import socket
import os
import struct
import pickle


class TCPServer:
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
listen_count = 5
max_recv_bytes = 8192
coding = 'utf-8'
allow_reuse_address = False
# 下载的文件存放路径
down_filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'share')
# 上传的文件存放路径
upload_filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'upload')

def __init__(self,server_address,bind_and_listen=True):
self.server_address = server_address
self.socket = socket.socket(self.address_family,self.socket_type)

if bind_and_listen:
try:
self.server_bind()
self.server_listen()
except Exception:
self.server_close()

def server_bind(self):
if self.allow_reuse_address: #重用ip和端口
self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
self.socket.bind(self.server_address)

def server_listen(self):
self.socket.listen(self.listen_count)

def server_close(self):
self.socket.close()

def server_accept(self):
return self.socket.accept()

def conn_close(self,conn):
conn.close()

def run(self):
print('starting...')
while True:
self.conn,self.client_addr = self.server_accept()
print(self.client_addr)
while True:
try:
res = self.conn.recv(self.max_recv_bytes)
if not res:continue
cmds = res.decode(self.coding).split()
if hasattr(self,cmds[0]):
func = getattr(self,cmds[0])
func(cmds)
except Exception:
break
self.conn_close(self.conn)

def get(self,cmds):
""" 下载
1.找到下载的文件
2.发送 header_size
3.发送 header_bytes file_size
4.读文件 rb 发送 send(line)
5.若文件不存在,发送0 client提示:文件不存在
:param cmds: 下载的文件 eg:['get','a.txt']
:return:
"""
filename = cmds[1]
file_path = os.path.join(self.down_filepath, filename)
if os.path.isfile(file_path):
header = {
'filename': filename,
'md5': 'xxxxxx',
'file_size': os.path.getsize(file_path)
}
header_bytes = pickle.dumps(header) #直接用pickle转为bytes,不用json+encode转为bytes
self.conn.send(struct.pack('i', len(header_bytes)))
self.conn.send(header_bytes)
with open(file_path, 'rb') as f:
for line in f:
self.conn.send(line)
else:
self.conn.send(struct.pack('i', 0))

def put(self,cmds):
""" 上传
1.接收4个bytes 得到文件的 header_size
2.根据 header_size 得到 header_bytes header_dic
3.根据 header_dic 得到 file_size
3.以写的形式 打开文件 f.write()
:param cmds: 下载的文件 eg:['put','a.txt']
:return:
"""
obj = self.conn.recv(4)
header_size = struct.unpack('i', obj)[0]
header_bytes = self.conn.recv(header_size)
header_dic = pickle.loads(header_bytes)
print(header_dic)
file_size = header_dic['file_size']
filename = header_dic['filename']

with open('%s/%s' % (self.upload_filepath, filename), 'wb') as f:
recv_size = 0
while recv_size < file_size:
res = self.conn.recv(self.max_recv_bytes)
f.write(res)
recv_size += len(res)


tcp_server = TCPServer(('127.0.0.1',8080))
tcp_server.run()
tcp_server.server_close()

  

客户端

import socket
import struct
import pickle
import os class FTPClient:
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
# 下载的文件存放路径
down_filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'download')
# 上传的文件存放路径
upload_filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'share')
coding = 'utf-8'
max_recv_bytes = 8192 def __init__(self, server_address, connect=True):
self.server_address = server_address
self.socket = socket.socket(self.address_family, self.socket_type)
if connect:
try:
self.client_connect()
except Exception:
self.client_close() def client_connect(self):
self.socket.connect(self.server_address) def client_close(self):
self.socket.close() def run(self):
while True:
# get a.txt 下载 put a.txt 上传
msg = input(">>>:").strip()
if not msg: continue
self.socket.send(msg.encode(self.coding))
cmds = msg.split()
if hasattr(self,cmds[0]):
func = getattr(self,cmds[0])
func(cmds) def get(self, cmds):
""" 下载
1.得到 header_size
2.得到 header_types header_dic
3.得到 file_size file_name
4.以写的形式 打开文件
:param cmds: 下载的内容 eg: cmds = ['get','a.txt']
:return:
"""
obj = self.socket.recv(4)
header_size = struct.unpack('i', obj)[0]
if header_size == 0:
print('文件不存在')
else:
header_types = self.socket.recv(header_size)
header_dic = pickle.loads(header_types)
print(header_dic)
file_size = header_dic['file_size']
filename = header_dic['filename'] with open('%s/%s' % (self.down_filepath, filename), 'wb') as f:
recv_size = 0
while recv_size < file_size:
res = self.socket.recv(self.max_recv_bytes)
f.write(res)
recv_size += len(res)
print('总大小:%s 已下载:%s' % (file_size, recv_size))
else:
print('下载成功!') def put(self, cmds):
""" 上传
1.查看上传的文件是否存在
2.上传文件 header_size
3.上传文件 header_bytes
4.以读的形式 打开文件 send(line)
:param cmds: 上传的内容 eg: cmds = ['put','a.txt']
:return:
"""
filename = cmds[1]
file_path = os.path.join(self.upload_filepath, filename)
if os.path.isfile(file_path):
file_size = os.path.getsize(file_path)
header = {
'filename': os.path.basename(filename),
'md5': 'xxxxxx',
'file_size': file_size
}
header_bytes = pickle.dumps(header)
self.socket.send(struct.pack('i', len(header_bytes)))
self.socket.send(header_bytes) with open(file_path, 'rb') as f:
send_bytes = b''
for line in f:
self.socket.send(line)
send_bytes += line
print('总大小:%s 已上传:%s' % (file_size, len(send_bytes)))
else:
print('上传成功!')
else:
print('文件不存在') ftp_client = FTPClient(('127.0.0.1',8080))
ftp_client.run()
ftp_client.client_close()

  

Python之路(第三十二篇) 网络编程:udp套接字、简单文件传输的更多相关文章

  1. Python之路(第三十四篇) 网络编程:验证客户端合法性

    一.验证客户端合法性 如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,那么利用hmac+加盐的方式来实现. 客户端验证的总的思路是将服务端随机产生的指定位数的字节发送到客 ...

  2. Python之路(第三十六篇)并发编程:进程、同步异步、阻塞非阻塞

    一.理论基础 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所有内容都是围绕进程的概念展开的. 即使可以利用的cpu只有一个(早期的 ...

  3. Python之路(第三十八篇) 并发编程:进程同步锁/互斥锁、信号量、事件、队列、生产者消费者模型

    一.进程锁(同步锁/互斥锁) 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理. 例 ...

  4. Python之路(第三十五篇) 并发编程:操作系统的发展史、操作系统的作用

    一.操作系统发展史 第一阶段:手工操作 —— 真空管和穿孔卡片 ​ 第一代之前人类是想用机械取代人力,第一代计算机的产生是计算机由机械时代进入电子时代的标志,从Babbage失败之后一直到第二次世界大 ...

  5. Python之路【第十二篇】:JavaScrpt -暂无内容-待更新

    Python之路[第十二篇]:JavaScrpt -暂无内容-待更新

  6. Python之路【第十二篇】:Python面向对象高级

    一.反射 1 什么是反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发了计算机科学领域关于应用反射性的研究 ...

  7. Python之路【第十二篇续】jQuery案例详解

    jQuery 1.jQuery和JS和HTML的关系 首先了HTML是实际展示在用户面前的用户可以直接体验到的,JS是操作HTML的他能改变HTML实际展示给用户的效果! 首先了解JS是一门语言,他是 ...

  8. Python之路【第二十二篇】:Django之Model操作

    Django之Model操作   一.字段 AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bi ...

  9. python之路【第十二篇】: MYSQL

    一. 概述 Mysql是最流行的关系型数据库管理系统,在WEB应用方面MySQL是最好的RDBMS(Relational Database Management System:关系数据库管理系统)应用 ...

随机推荐

  1. 69.纯 CSS 创作一个单元素抛盒子的 loader

    原文地址:https://segmentfault.com/a/1190000015470411#articleHeader0 HTML code: <div class="loade ...

  2. 稀疏矩阵 part 3

    ▶ 各种稀疏矩阵数据结构下 y(n,1) = A(n,m) * x(m,1) 的实现,CPU版本 ● MAT 乘法 int dotCPU(const MAT *a, const MAT *x, MAT ...

  3. strace参数

    strace参数 -c 统计每一系统调用的所执行的时间,次数和出错的次数等. -d 输出strace关于标准错误的调试信息. -f 跟踪由fork调用所产生的子进程. -ff 如果提供-o filen ...

  4. Delphi Exif

    这久要用到读取JPG 的 Exif 信息,先是在盒子里下了个Demo,但是那个太老了,是2003年的,后来下载了个CCR 1.5.1是可以使用了,但是我个人用的是Delphi 2007,似乎CCR 1 ...

  5. Zabbix11.3 Zabbix SNMP 常用OID列表

    点击获取CISCO设备OID 系统参数(1.3.6.1.2.1.1) OID 描述 备注 请求方式 .1.3.6.1.2.1.1.1.0 获取系统基本信息 SysDesc GET .1.3.6.1.2 ...

  6. Logistic Regression(逻辑回归)

    分类是机器学习的一个基本问题, 基本原则就是将某个待分类的事情根据其不同特征划分为两类. Email: 垃圾邮件/正常邮件 肿瘤: 良性/恶性 蔬菜: 有机/普通 对于分类问题, 其结果 y∈{0,1 ...

  7. [C++]数组指针,数组引用,函数指针

    数组指针是指一个指向数组的指针,例如有一个数组指针p指向一个数组a[],则 *p是取到这个数组,也就是说 *p=a,因此 **p =a[0], 它的定义为: ]; ]=&a; (*c)表示它是 ...

  8. 这就涉及到ABAQUS历史输出中各能量变量的意义

    ABAQUS中,对于很多动态问题,尤其像高速冲击模拟中,对结果评价很重要的一点就是要保证模型能量守恒,这就涉及到ABAQUS历史输出中各能量变量的意义,下面最各简单整理: ALLAE:人工伪应变能,六 ...

  9. 基于WCF的支持跨局域网可断点续传的大文件传输服务实现

    题外话:这个系列的文章记录了本人最近写的一个小工程,主要包含了两个功能,一是对文件的断点续传的功能,二是基于WCF的一对多文件主动发送的功能,顺便这也是我自己在WCF学习路上的一个小成果吧. 在网上找 ...

  10. week07 codelab02 C72

    ss 我们要改一下backendserver的service 因为要写几个api还要做很多操作 我们单独写出来 然后由service来调用 import json import os import p ...