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. ES6-Generator使用与改写

    用Generator封装Symbol中的iterator方法: 注意:Generator的function后必须写* config:分别有3个txt文件,两个文件写路径,一个文件写要输出的内容 前置写 ...

  2. 【转】Web实现前后端分离,前后端解耦

    一.前言 ”前后端分离“已经成为互联网项目开发的业界标杆,通过Tomcat+Ngnix(也可以中间有个Node.js),有效地进行解耦.并且前后端分离会为以后的大型分布式架构.弹性计算架构.微服务架构 ...

  3. [Gamma]Scrum Meeting#6

    github 本次会议项目由PM召开,时间为6月1日晚上10点30分 时长10分钟 任务表格 人员 昨日工作 下一步工作 木鬼 撰写博客,组织例会 撰写博客,组织例会 swoip 前端显示屏幕,翻译坐 ...

  4. 【Activiti学习之八】Spring整合Activiti

    环境 JDK 1.8 MySQL 5.6 Tomcat 8 idea activiti 5.22 activiti-explorer是官方提供的一个演示项目,可以使用页面管理Activiti流程.ac ...

  5. 解决myeclipse2017 properties中文被Unicode编码

    输入:http://propedit.sourceforge.jp/eclipse/updates/ 在线安装插件解决.

  6. python 统计字符串中指定字符出现次数的方法

    python 统计字符串中指定字符出现次数的方法: strs = "They look good and stick good!" count_set = ['look','goo ...

  7. mysql 添加注释

    给表添加注释:  alter table m_tb100_disabledsoldier comment '残疾士兵';   给表中的字段添加注释alter table m_tb100_disable ...

  8. python入门之名称空间的查找顺序

    名称空间的查找顺序: 局部:局部 > 全局 > 内置 全局:全局 > 内置 # 内置再找不到就报错 # 函数内部使用的名字,在定义阶段已经规定死了,与你的调用位置无关 x = 111 ...

  9. Xamarin.Android DatePickerFragment 日期控件

    MainActivity 代码: public class MainActivity : Activity { TextView _dateDisplay; Button _dateSelectBut ...

  10. 快排的java实现方式,用java代码来实现快排

    1. 快排的思想 通过一趟排序将要排序的数据分割成独立的两部分,前一部分的所有数据都要小于后一部分的所有数据,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据的 ...