网络编程之UDP套接字(四)

news2024/11/26 9:45:01

12. 基于 UDP 的套接字

UDP 是无链接的,可以实现服务端与多个客户端进行同时进行通讯,无论先启动哪一端都可以。(qq 聊天 UDP 服务)

12.1 基于 udp 套接字模板

UDP 服务端

s = socket(AF_INET, SOCK_DGRAM)		# 创建一个服务器的套接字
s.bind()	# 绑定服务器套接字
inf_loop:	# 服务器无限循环
    data,addr = s.recvfrom(buffer_size)		# 接收
    s.sendto(data.upper(), addr)	# 发送
s.close()	# 关闭服务器套接字

UDP 客户端

s = socket(AF_INET, SOCK_DGRAM)		# 创建客户端套接字
comm_loop:		# 通讯循环
    s.sendto('xxx', ip_port)	# 发送
    data,addr = s.recvfrom(2024)	# 接收
s.close()	# 关闭客户端套接字

示例:

服务端

from socket import *

ip_port = (gethostname(), 8080)
buffer_size = 1024

udp_server = socket(AF_INET, SOCK_DGRAM)
udp_server.bind(ip_port)

while True:
    conn, addr = udp_server.recvfrom(buffer_size)
    print('客户端发来信息:', conn.decode('utf-8'))
    udp_server.sendto(conn.upper(), addr)

udp_server.close()

客户端

from socket import *

ip_port = (gethostname(), 8080)
buffer_size = 1024

udp_client = socket(AF_INET, SOCK_DGRAM)

while True:
    msg = input('>>>').strip()
    udp_client.sendto(msg.encode('utf-8'), ip_port)
    data, addr = udp_client.recvfrom(buffer_size)
    print('服务端发来信息:', data.decode('utf-8'))
udp_client.close()

12.2 基于 UDP 实现 ntp 服务

ntp 服务(Network Time Protocol),是用来使计算机时间同步化的一种协议。

需求:基于 UDP 服务实现 ntp 服务,获取服务端时间:

服务端:

from socket import *
import time

ip_port = (gethostname(), 8080)
buffer_size = 1024

ntp_server = socket(AF_INET, SOCK_DGRAM)
ntp_server.bind(ip_port)

while True:
    data, addr = ntp_server.recvfrom(buffer_size)  # data: 客户端发来的信息  addr:客户端地址
    print('客户端时间格式:', data.decode('utf-8'))

    if not data:  # 如果客户端输入的为空
        fmt = '%Y-%m-%d %X'
    else:
        fmt = data.decode('utf-8')  # 客户端自定义的时间格式
    back_time = time.strftime(fmt)
    ntp_server.sendto(back_time.encode('utf-8'), addr)

ntp_server.close()

客户端:

from socket import *

ip_port = (gethostname(), 8080)
buffer_size = 1024

ntp_client = socket(AF_INET, SOCK_DGRAM)

while True:
    msg = input('>>>')		# 客户端输入时间格式
    ntp_client.sendto(msg.encode('utf-8'), ip_port)
    data, addr = ntp_client.recvfrom(buffer_size)
    print('服务端时间:', data.decode('utf-8'))

ntp_client.close()

13. socketserver 实现并发

利用 socketserver 模块可以实现基于 tcp 服务的套接字并发功能。基于 tcp 的套接字主要有两个关键循环:一个链接循环,一个通讯循环。

sockeserver 模块分为两大类:server 类(解决链接问题),request 类(解决通讯类)。

server 类

  • 与链接相关:BaseServer、TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer
  • 基于多线程实现并发:ThreadingMixIn、ThreadingTCPServer、ThreadingUDPServer
  • 基于多进程实现并发:ForkingMinIn、ForkingTCPServer、ForkingUDPServer

request 类

BaseRequestHandler、StreamRequestHandler、DatagramRequestHandler

继承关系

源码分析

  • 基于 tcp 的 sockserver 我们自己定义的类中的:
    • self.server:套接字对象
    • self.request:一个链接
    • self.client_address:客户端地址
  • 基于 udp 的 sockserver 我们自己定义的类中的:
    • self.request:是一个元组,第一个元素是客户端发来的数据,第二个元素是服务端的 udp 套接字对象
    • self.client_address:客户端地址

示例

TCP 实现并发

服务端:

import socketserver

ip_port = ('127.0.0.1', 8080)
buffer_size = 1024


class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        print('客户端地址', self.client_address)
        print('客户端链接', self.request)

    # 通讯循环
        while True:
            try:
                # 收消息
                data = self.request.recv(1024)
                if not data :break
                print('收到客户端%s的消息是:%s' %(self.client_address, data.decode('utf-8')))

                # 发消息
                self.request.send(data.upper())
            except Exception as e:
                print(e)

# 链接循环
if __name__ == '__main__':
    # 内部会产生一个 socket 对象,并连接客户端
    s = socketserver.ThreadingTCPServer(ip_port, MyServer)  # 第一个参数:ip_port,第二个:自定义的类名
    s.serve_forever()

客户端:

from socket import *

ip_port = ('127.0.0.1', 8080)
buffer_size = 1024

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

while True:
    msg = input(">>>").strip()
    if not msg:continue
    if msg == 'quit':break
    client.send(msg.encode('utf-8'))

    data = client.recv(buffer_size)

    print('收到服务端的消息', data.decode('utf-8'))

client.close()

socketserver 模块利用进程、线程来实现并发功能,其内部封装了很多类(如:创建socket对象,实现链接循环,以及利用进程、线程实现并发)。我们利用这些接口去实现通讯循环即可。

我们必须定义一个自己的类,并实现 handle() 方法 。需要注意的是我们不需要修改客户端。

UDP 实现并发

服务端:

import socketserver

ip_port = ('127.0.0.1', 8080)


class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        print(self.request)
        # self.request: (b'ahhd', <socket.socket fd=216, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
        # 第一个元素:客户端发来的消息,第二个:服务端 socket 对象
        
        print('收到客户端消息是', self.request[0])
        self.request[1].sendto(self.request[0].upper(), self.client_address)

if __name__ == '__main__':
    s = socketserver.ThreadingUDPServer(ip_port, MyServer)
    s.serve_forever()

客户端:

from socket import *

ip_port = ('127.0.0.1', 8080)
buffer_size = 1024

client = socket(AF_INET, SOCK_DGRAM)

while True:
    msg = input('>>>').strip()
    if not msg:continue
    if msg == 'quit':break
    client.sendto(msg.encode('utf-8'), ip_port)

    data, addr = client.recvfrom(buffer_size)
    print('服务端发来消息', data.decode('utf-8'))

client.close()

14. 认证客户端链接合法性

如果想在分布式系统中实现一个简单的客户端链接认证功能,又不像 SSL 那么复杂,那么可以采用 hmac(算法加密)+ 盐(随机密码)的方法来实现。

当服务端受到 syn 洪水攻击(即攻击者利用很多客户端发送空信息,造成服务端奔溃)时,验证客户端链接合法性很有必要。

服务端:

# _*_coding:utf-8_*_
__author__ = 'Hubery_Jun'

from socket import *
import hmac, os

# 验证 key,即 salt
secret_key = b'Life is Short, you need Python'


def conn_auth(conn):
    """
    验证链接合法性
    :param conn:
    :return:
    """
    print('开始验证链接合法性')
    msg = os.urandom(32)    # 随机产生一个 32 字节长的字节对象,用于加密
    conn.sendall(msg)       # 发送给客户端验证
    
    h = hmac.new(secret_key, msg)   # 使用 hmac 模块(msg + salt)进行算法加密
    digest = h.digest()
    res = conn.recv(len(digest))    # 接收客户端的验证信息
    return hmac.compare_digest(res, digest)  # 两者比较,相同返回 True


def data_handler(conn, buffer_size=1024):
    """
    接收数据
    :param conn:链接
    :param buffer_size:接收数据大小
    :return:
    """
    if conn_auth(conn):
        print('链接不合法')
        conn.close()
        return
    print('链接合法,开始通讯')
    while True:
        data = conn.recv(buffer_size)
        if not data:break
        print('客户端发来消息', data.decode('utf-8'))
        conn.sendall(data.upper())


def server_handler(ip_port, buffer_size, back_log=5):
    """
    处理链接
    :param ip_port: ip + port
    :param buffer_size: 接收数据大小
    :return:
    """
    s = socket(AF_INET, SOCK_STREAM)
    s.bind(ip_port)
    s.listen(back_log)
    while True:
        conn, addr = s.accept()
        print('客户端链接', addr)
        data_handler(conn, buffer_size)

if __name__ == '__main__':
    ip_port = (gethostname(), 8080)
    buffer_size = 1024
    server_handler(ip_port, buffer_size)

客户端:

# _*_coding:utf-8_*_
__author__ = 'Hubery_Jun'

# 验证密码
secret_key = b'Life is Short, you need Python'


from socket import *
import hmac


def conn_auth(client):
    """
    验证客户端到服务端的链接
    :param client:
    :return:
    """
    msg = client.recv(32)
    h = hmac.new(msg, secret_key)
    digest = h.digest()
    client.sendall(digest)


def client_handler(ip_port, buffer_size):
    """
    通讯循环
    :param ip_port: ip + port
    :param buffer_size: 数据大小
    :return:
    """
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(ip_port)

    conn_auth(client)

    while True:
        data = input('>>>').strip()
        if not data:continue
        if data == 'quit':break

        client.sendall(data.encode('utf-8'))
        res = client.recv(buffer_size)
        print('服务端发来消息', res.decode('utf-8'))
    client.close()

if __name__ == '__main__':
    ip_port = (gethostname(), 8080)
    buffer_size = 1024
    client_handler(ip_port, buffer_size)
客户端链接 ('192.168.152.1', 9866)
开始验证链接合法性
链接合法,开始通讯
客户端发来消息 dir
  • 服务端: 利用 os.urandom(32) 产生一个随机的 32字节长度的字节对象 msg,并发送给客户端验证。自己随便编写一个 key,然后利用 hamc 模块进行算法加密。
  • 客户端: 接收服务端发来的 msg,再利用 hmac 模块进行算法加密,然后再把加密后的信息发送给服务端,进行比较。如客户端发过去的信息和服务端加密后的信息相符,则返回 True,链接合法,否则不合法,直接关闭链接。

当客户端不知道 key 时,或者不知道加密方法时,向服务端发起链接请求,就会验证失败。

  • AF_INET:基于网络
  • SOCK_DGRAM:基于 UDP ,数据报,无链接
  • SOCK_STREAM:基于 TCP,数据流
  • 发送数据要是字节形式(二进制)
  • udp 发送数据,后面要跟 host+port
  • udp 接收数据,接收的是个元组(数据,主机+port)
  • udp 可以接收多个客户端同时
  • tcp:不能实现并发,只能同时服务一个客户端
  • udp:可以实现并发,能同时服务多个客户端,因为它没有链接
  • recvfrom 接受空也不会报错

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/393501.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL运维篇之分库分表

03、分库分表 3.1、介绍 3.1.1、问题分析 随着互联网及移动互联网的发展&#xff0c;应用系统的数据量也是呈指数式增长&#xff0c;若采用单数据库进行数据存储&#xff0c;存在以下性能瓶颈&#xff1a; IO瓶颈&#xff1a;热点数据太多&#xff0c;数据库缓存不足&#…

脑机接口科普0012——脑机接口的灭亡

本文禁止转载&#xff01;&#xff01;&#xff01;&#xff01;中国人比较好玩&#xff0c;好玩就好玩在&#xff0c;对于每个行业&#xff0c;都喜欢说一句话&#xff0c;XXX从入门到放弃&#xff01;我们才科普到第12小节&#xff0c;然后&#xff0c;我们这边就说到脑机接口…

Vue 使用 Export2Excel.js 导出多 sheet 的 excel

项目需求&#xff0c;导出多sheet的excel表格。 具体思路是&#xff1a;后端返回json数据&#xff0c;前端根据数据和具体的几项字段去导出excel表格&#xff0c;多sheet&#xff0c;多页表格到一个excel表里面&#xff0c;具体思路 根据Export2Excel插件&#xff0c;并修改插件…

NFTScan:怎么使用 NFT API 开发一个 NFT 数据分析平台?

对很多开发者来说&#xff0c;在 NFT 数据海洋中需要对每个 NFT 进行索引和筛选是十分困难且繁琐的&#xff0c;NFT 数据获取仍是一大问题。而数据平台提供的 API 使得开发者可以通过接口获取区块链上 NFT 的详细信息&#xff0c;并对其进行分析、处理、统计和可视化。在本篇文…

今天女神节,用python画个贺卡送给母亲吧

今天女神节&#xff0c;你给女神妈妈准备了什么祝福呢&#xff1f;如果还没有&#xff0c;那么画个贺卡送给她吧&#xff0c;在你眼里&#xff0c;她是一个什么样的人呢&#xff1f; 是"可爱"&#xff0c;“温柔”&#xff0c;“美丽”&#xff0c;“漂亮”&#xf…

OpenCV各模块函数使用实例(11)--- 矩阵和数组操作(Operations on arrays)

OpenCV各模块函数使用实例&#xff08;11&#xff09;M、矩阵和数组操作&#xff08;Operations on arrays&#xff09;本节描述矩阵的基本操作&#xff0c;这些操作是图像处理和其他数组算法实现的基本操作&#xff0c;包括矩阵的运算&#xff0c;特征值和特征向量&#xff0c…

详细介绍关于链表【数据结构】

文章目录链表单链表尾插头插尾删第一种方式删除第二种头删查找pos之前插入pos位置删除pos后面插入pos位置后面删除链表 顺序表缺点&#xff1a; 空间不够了 需要扩容&#xff0c;但是扩容是有消耗的头部或中间位置需要插入或删除&#xff0c;需要挪动 &#xff0c;但是挪动是…

如何让AI帮你干活-娱乐(3)

背景今天的话题会偏代码技巧一些&#xff0c;对于以前没有接触过代码的朋友或者接触代码开发经验较少的朋友会有些吃力。上篇文章介绍了如何广视角的生成相对稳定的视频。昨天的实现相对简单&#xff0c;主要用的是UI界面来做生成。但是生成的效果其实也显而易见&#xff0c;不…

汽车娱乐系统解决方案

Danlaw在汽车和航空航天行业里是全球知名的技术和服务供应商&#xff0c;致力于提供更加安全与智能的系统。Danlaw以突破性技术和高效开发、动态环境的自适应解决方案而闻名。Danlaw优秀的联网汽车解决方案使之成为全球大型互联设备供应商之一。 一 信息娱乐系统测试 | 风丘科…

力扣-查找每个员工花费的总时间

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;1741. 查找每个员工花费的总时间二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行…

妙算(manifold-2G)通过网线连接笔记本实现静态ip上网

本质就是共享笔记本wifi&#xff0c;配置以太网静态ip&#xff0c;然后在工控机上连接 参考&#xff1a; https://zhuanlan.zhihu.com/p/378814739 https://blog.csdn.net/weixin_42128941/article/details/98635554 设备 笔记本电脑win10妙算ubuntu16.04网线 首先&#xff0…

RTOS中的消息队列的原理以及应用

消息队列的原理 RTOS中的消息队列是一种用于任务间通信的机制&#xff0c;它可以实现任务之间的异步通信&#xff0c;从而实现任务间的解耦。消息队列是一个先进先出的数据结构&#xff0c;任务可以向队列中发送消息&#xff0c;也可以从队列中接收消息。 消息队列的原理如下…

ChatGPT技术与商业模式及产业发展布局方案

文章目录模块一&#xff1a;概念模块二&#xff1a;架构模块三&#xff1a;技术模块四&#xff1a;算力模块五&#xff1a;体验模块六&#xff1a;应用模块七&#xff1a;商业模块八&#xff1a;产业模块九&#xff1a;建议结语主要内容&#xff1a; 采用模块化教学方法&#x…

【数据结构】二叉树与堆

文章目录1.树概念及结构1.1树的相关概念1.2树的结构2.二叉树概念及结构2.1相关概念2.2特殊的二叉树2.3二叉树的性质2.4二叉树的存储结构3.二叉树的顺序结构及实现3.1二叉树的顺序结构3.2堆的概念3.3堆的实现Heap.hHeap.c3.4堆的应用3.4.1 堆排序3.4.2 TOP-KOJ题最小K个数4.二叉…

ESXI主机安装Zabbix 6.2

1&#xff1a;首先下载Zabbix Appliance 2&#xff1a;还需要下载VMware converter &#xff0c;这个需要VMware的账号&#xff0c;或者从其他地方下载也可以。 3:vmdk格式的 image 可直接在 VMware Player、 Server 和 Workstation 产品中使用。要在 ESX,、ESXi 和 vSphere 中…

下一站,冠军|走进2022 OceanBase数据库大赛12强

欢迎访问OceanBase官网获取更多信息&#xff1a;https://www.oceanbase.com/ 还记得 2021 OceanBase 数据库大赛中 令人羡慕的神雕侠侣 “NoPassCET4”吗&#xff1f; 打比赛&#xff0c;也打蚊子的“两只老虎吃萝卜”吗&#xff1f; 一个人扛起一支队伍的 “lying_flat”吗…

小孩用的台灯什么样的品牌好?2023儿童台灯灯具品牌排行榜

现代社会高速发展&#xff0c;孩子小小年纪就使用上了各种电子产品&#xff0c;加上学习压力较大&#xff0c;近视问题也普遍比较严重。据统计&#xff0c;我国青少年儿童近视率常年维持在50%以上&#xff0c;半数以上的孩子都患有近视&#xff0c;高居世界第一&#xff0c;情况…

c---冒泡排序模拟qsort

一、冒泡排序 二、冒泡排序优化排各种类型数据 文章目录一、冒泡排序二、冒泡排序优化排各种类型数据冒泡排序 冒泡排序原理&#xff1a;两两相邻元素进行比较 初级版 void bulle_sort(int* a, int sz) {int i 0;for (int i 0; i < sz-1; i){int j 0; for (j 0; j…

阿里大佬翻遍全网Java面试文章,总结出这份1658页文档,GitHub收获25K+点赞

就目前大环境来看&#xff0c;跳槽成功的难度比往年高很多。一个明显的感受&#xff1a;今年的面试&#xff0c;无论一面还是二面&#xff0c;都很考验Java程序员的技术功底。这不又到了面试跳槽的黄金段&#xff0c;成功升职加薪&#xff0c;不成功饱受打击。当然也要注意&…

使用日历丰富产品的用户体验

前言 经过一段时间的梳理和遴选&#xff0c;我挑选出了Android知识图谱中重要的部分&#xff0c;制作了一张脑图。读者朋友们可按照脑图查漏补缺了&#xff0c; 图片尺寸较大&#xff0c;仅附链接 。 当然&#xff0c;这是我按照自己的判断、结合参考其他博主的观点进行的挑选…