Socket网络编程-UDP编程

                                   作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.UDP编程概述

1>.UDP服务端编程流程

  创建socket对象。socket.SOCK_DGRAM 

  绑定IP和Port,bind()方法

  传输数据
    接收数据,socket.recvfrom(bufsize[, flags]),获得一个二元组(string, address)
    发送数据,socket.sendto(string, address) 发给某地址某信息   释放资源

2>.UDP客户端编程流程

  创建socket对象。socket.SOCK_DGRAM
  发送数据,socket.sendto(string, address)发给某地址某信息
    
  接收数据,socket.recvfrom(bufsize[, flags]),获得一个二元组(string, address)   释放资源

3>.UDP编程中常用的方法

  bind方法
    可以指定本地地址和端口laddr,会立即占用

  connect方法
    可以立即占用本地地址和端口laddr,填充远端地址和端口raddr

  sendto方法
    可以立即占用本地地址和端口laddr,并把数据发往指定远端。只有有了本地绑定端口,sendto就可以向任何远端发送数据

  send方法
    需要和connect方法配合,可以使用已经从本地端口把数据发往raddr指定的远端

  recv方法
    要求一定要在占用了本地端口后,返回接收的数据

  recvfrom方法
    要求一定要占用了本地端口后,返回接收的数据和对端地址的二元组

4>.心跳机制

  增加心跳heartbeat机制或ack机制。这些机制同样可以用在TCP通信的时候。 心跳,就是一端定时发往另一端的信息,一般每次数据越少越好。心跳时间间隔约定好就行。 ack即响应,一端收到另一端的消息后返回的确认信息。

  心跳机制实现策略:
    1.一般来说是客户端定时发往服务端的,服务端并不需要ack回复客户端,只需要记录该客户端还活 着就行了。当然服务端也可响应客户端
    2.如果是服务端定时发往客户端的,一般需要客户端ack响应来表示活着,如果没有收到某客户端的ack响应,服务端移除其信息。这种实现较为复杂,用的较少
    3.也可以双向都发心跳的,用的更少

二.UDP版群聊案例

1>.UDP版群聊服务端代码

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import socket
import threading
import datetime
import logging FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
logging.basicConfig(format=FORMAT, level=logging.INFO) #在服务器端代码中使用第一种心跳机制改进
class ChatUDPServer:
def __init__(self, ip='127.0.0.1', port=6688, interval=10):
self.addr = (ip, port)
self.sock = socket.socket(type=socket.SOCK_DGRAM)
self.clients = {} # 记录客户端,改为字典
self.event = threading.Event()
self.interval = interval # 默认10秒,超时就要移除对应的客户端 def start(self):
self.sock.bind(self.addr) # 立即绑定
# 启动线程
threading.Thread(target=self.recv, name='recv').start() def recv(self):
removed = set() # 超时的
while not self.event.is_set():
data, raddr = self.sock.recvfrom(1024) # 阻塞接收数据
current = datetime.datetime.now().timestamp() # float
if data.strip() == b'^hb^': # 心跳信息
print('^^^^^^^^hb', raddr)
self.clients[raddr] = current
continue
elif data.strip() == b'quit':
#有可能发来数据的不在clients中
self.clients.pop(raddr, None)
logging.info('{} leaving'.format(raddr))
continue #有信息来就更新时间
#什么时候比较心跳时间呢? 发送信息的时候,反正要遍历一遍
self.clients[raddr] = current
msg = '{}. from {}:{}'.format(data.decode(), *raddr)
logging.info(msg)
msg = msg.encode() for c, stamp in self.clients.items():
if current - stamp > self.interval:
removed.add(c)
else:
self.sock.sendto(msg, c) # 不保证对方能够收到 for c in removed:
self.clients.pop(c)
removed.clear() def stop(self):
self.event.set()
self.clients.clear()
self.sock.close() def main():
server = ChatUDPServer()
server.start() while True:
cmd = input(">>> ")
if cmd.strip() == 'quit':
server.stop()
break
logging.info(threading.enumerate())
logging.info(server.clients) if __name__ == '__main__':
main()

2>.UDP版群聊客户端代码

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import threading
import socket
import logging FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
logging.basicConfig(format=FORMAT, level=logging.INFO) #增加定时发送心跳代码
class ChatUdpClient:
def __init__(self, rip='127.0.0.1', rport=6688):
self.sock = socket.socket(type=socket.SOCK_DGRAM)
self.raddr = (rip, rport)
self.event = threading.Event() def start(self):
self.sock.connect(self.raddr) # 占用本地地址和端口,设置远端地址和端口
threading.Thread(target=self._sendhb, name='heartbeat', daemon=True).start()
threading.Thread(target=self.recv, name='recv').start() def _sendhb(self): # 心跳
while not self.event.wait(5):
self.send('^hb^') def recv(self):
while not self.event.is_set():
data, raddr = self.sock.recvfrom(1024)
msg = '{}. from {}:{}'.format(data.decode(), *raddr)
logging.info(msg) def send(self, msg:str):
self.sock.send(msg.encode())
# self.sock.sendto(msg.encode(), self.raddr) def stop(self):
self.event.set()
self.send('quit') #通知服务端退出
self.sock.close() def main():
client1 = ChatUdpClient()
client2 = ChatUdpClient()
client1.start()
client2.start()
print(client1.sock)
print(client2.sock) while True:
cmd = input('Input your words >> ')
if cmd.strip() == 'quit':
client1.stop()
client2.stop()
break
client1.send(cmd)
client2.send(cmd) if __name__ == '__main__':
main()

三.UDP协议应用

  UDP是无连接协议,它基于以下假设:
    网络足够好
    消息不会丢包
    包不会乱序

  但是,即使是在局域网,也不能保证不丢包,而且包的到达不一定有序。

  应用场景:
    视频、音频传输,一般来说,丢些包,问题不大,最多丢些图像、听不清话语,可以重新发话语来解 决。
    海量采集数据,例如传感器发来的数据,丢几十、几百条数据也没有关系。 DNS协议,数据内容小,一个包就能查询到结果,不存在乱序,丢包,重新请求解析。
    一般来说,UDP性能优于TCP,但是可靠性要求高的场合的还是要选择TCP协议。 DNS使用的就是UDP协议和TCP协议。

Socket网络编程-UDP编程的更多相关文章

  1. 37 - 网络编程-UDP编程

    目录 1 UDP协议 2 UDP通信流程 3 UDP编程 3.1 构建服务端 3.3 常用方法 4 聊天室 5 UDP协议应用 1 UDP协议 UDP是面向无连接的协议,使用UDP协议时,不需要建立连 ...

  2. 五十六、linux 编程——UDP 编程模型

    56.1 UDP 编程模型 56.1.1 编程模型 UDP 协议称为用户数据报文协议,可靠性比 TCP 低,但执行效率高 56.1.2 API (1)发送数据 函数参数: sockfs:套接字文件描述 ...

  3. socket与TCP/UDP编程~

    ket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序.要学Internet上的TCP/IP网络编程,必须理解Socket接口. ...

  4. python的socket编程之udp编程

    在上篇文章中,仅仅讲述了如何进行了TCP编程,在本章中,将讲述使用udp进行编码,先看如下的代码,服务器端: root@python 513]# cat serverudp.py #!/usr/bin ...

  5. UNIX网络编程——UDP编程模型

    使用UDP编写的一些常见得应用程序有:DNS(域名系统),NFS(网络文件系统)和SNMP(简单网络管理协议). 客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目 ...

  6. 网络编程——UDP编程

    一个简单的聊天代码:运行结果: 在这个程序之中,由于recvfrom函数拥塞函数,没有数据时会一直阻塞,所以客户端和服务器端只能通过一回一答的方式进行信息传递.严格的讲UDP没有明确的客户端和服务端, ...

  7. 五十五 网络编程 UDP编程

    TCP是建立可靠连接,并且通信双方都可以以流的形式发送数据.相对TCP,UDP则是面向无连接的协议. 使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包.但是,能不 ...

  8. 五十八、linux 编程——UDP 编程 广播

    58.1 广播介绍 58.1.1 介绍 广播实现一对多的通讯 它通过向广播地址发送数据报文实现的 58.1.2 套接字选项 套接字选项用于修饰套接字以及其底层通讯协议的各种行为.函数 setsocko ...

  9. 五十七、linux 编程——UDP 编程 域名解析

    57.1 介绍 57.1.1 域名解析 57.1.2 域名解析函数 gethostent 可以获取多组,gethostbyname 只可以获取一组 /etc/hosts 文件设置了域名和 IP 的绑定 ...

随机推荐

  1. 腾讯云VPS注意事项

    这几天腾讯云VPS搞活动 买了2台服务器, 1台是1核2G1M带宽,一年99 1台是2核4G6M带宽,三年1499 前几年一直在用阿里云,感觉价格太贵,价格上腾讯云,搞活动真的优惠比较大, 最近也准备 ...

  2. IDEA Gradle配置与使用

    1.安装Gradle,并添加环境变量. https://www.cnblogs.com/NyanKoSenSei/p/11458953.html 2.在IDEA中设置Gradle: 3.选中项目中的. ...

  3. NamedParameterJdbcTemple与RowMapper实现

    NamedParameterJdbcTemplate和JdbcTemplate功能基本差不多. 1.配置 db.properties 1 jdbc.user=root 2 jdbc.password= ...

  4. Appium 滑动踩坑记

    前言 对于不同java-client版本,很多的API已经产生大的变化,所以一些API大家会发现已经失效或者使用方式发生了变化,滑动就是其中一项,这篇文章对滑动在不同的java-client版本以及不 ...

  5. Python【每日一问】22

    问: [基础题]:输出 9*9 口诀表 [提高题]:古典问题:有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? ...

  6. ng打包

    1.在项目完成后进行项目打包 2.输入ng build后会出现dist打包文件 3.在打包文件中有一个文件index.html文件,改变html中的一个参数 <base href="/ ...

  7. Elasticsearch Transport 模块创建及启动分析

    Elasticsearch 通信模块的分析从宏观上介绍了ES Transport模块总体功能,于是就很好奇ElasticSearch是怎么把服务启动起来,以接收Client发送过来的Index索引操作 ...

  8. 记lombok@Data和@Builder一起用无法添加无参构造方法的坑

    转自:https://blog.csdn.net/w605283073/article/details/89221853 今天和小伙伴讨论一个mybatis-plus的一个诡异问题,最后定位到原因竟然 ...

  9. Scala Types 1

    在 Scala 中所有值都有一种对应的类型 单例类型 形式:value.type,返回类型 value / null 场景1:链式API调用时的类型指定 class Super { def m1(t: ...

  10. FusionInsight大数据开发---Redis应用开发

    Redis应用开发 要求: 了解Redis应用场景 掌握Redis二次开发环境搭建 掌握Redis业务开发 Redis简介 Redis是一个基于网络的,高性能key-value内存数据库 Redis根 ...