概述

使用TCP编写的应用程序和使用UDP编写的应用程序之间存在一些本质差异,其原因在于这两个传输层之间的差别:UDP是无连接不可靠的数据报协议,非常不同于TCP提供的面向连接的可靠字节流。然而相比TCP,有些场合更适合UDP。使用UDP编写的一些常见应用程序有:DNS(域名系统)、NFS(网络文件系统)和SNMP(简单网络管理协议)。

下图给出了典型的UDP客户/服务器程序的函数调用。客户不必与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地(即服务器)的地址作为参数。类似的,服务器不接受来自客户的连接,而是只管调用recvfrom函数,等待来自某个客户的数据到达。recvfrom将于所接受的数据报一道返回客户的协议地址,因此服务器可以把响应发送给正确的客户。

recvfrom和sendto函数

这个函数类似于标准的read和write函数,不过需要三个额外的参数:

#include<sys/socket.h>

ssize_t recvfrom(int sockfd, void *buf, size_t nbytes, int flags,
struct sockaddr *from, socklen_t *addrlen); ssize_t sendto(int sockfd, const void* buf, size_t nbytes, int flags,
const struct sockaddr* to, sockelen_t addrlen);

前三个参数sockfd,buf和nbytes等同于read和write函数的三个参数:描述符、指向读入或写出缓冲区的指针和读写字节数。

sendto的to参数指向一个含有数据报接收者的协议地址(如IP地址和端口号)的套接字地址结构,其大小是由addrlen参数指定。recvfrom的from参数指向一个将由该函数在返回时填写的数据报发送者的协议地址的套接字地址结构,而在该套接字结构中填写的字节数则在addrlen参数所指的整数中返回给调用者。注意,sendto的最后一个参数是一个整数值,而recvfrom的最后一个参数是一个指向整数值的指针(即值-结果参数)

recvfrom的最后两个参数类似于accept的最后两个参数:返回时其中套接字的地址结构告诉我们是谁发送了数据报(UDP情况下)或是谁发起了连接(TCP情况下)。sendto的最后两个参数类似于connect的最后两个参数:调用时其中套接字的地址结构被我们填入数据报将发往(UDP情况下)或与之建立连接(TCP情况下)的协议地址。

这两个函数都把所读写的数据的长度作为函数返回值。在recvfrom使用数据报协议的典型用途中,返回值就是所接收数据报中的用户数据量。

写一个长度为0的数据报是可行的。在UDP情况下,这会形成一个只包含一个IP首部(对于IPV4通常为20字节,对于IPV6通常是40字节)和一个8字节的UDP首部而没有数据的IP数据报。这也意味着对于数据报协议,recvfrom返回0值是可以接受的:它不像TCP套接字上read返回0值那样表示对端已关闭连接。既然UDP是无连接的,因此也就没有诸如关闭一个UDP连接之类的事情。

如果recvfrom的from是一个空指针,那么相应的长度参数(addrlen)也必须是一个空指针,表示我们并不关心数据报发送者的协议地址。

UDP回射服务器

一般来说,大多数TCP服务器是并发的,而大多数UDP服务器是迭代的。

事实上每个UDP套接字都有一个接收缓冲区,到达该套接字的每个数据报都进入这个套接字的接收缓冲区。这样,在进程能够读该套接字任何已排好队的数据报之前,如果有多个数据报到达该套接字,那么相继到达的数据报仅仅加到该套接字的接收缓冲区中。然而这个缓冲区的大小是由限制的

UDP的connect函数

可以给UDP套接字调用connect,然而这样做的结果与TCP连接大相径庭:没有三路握手过程。内核只是检查是否存在立即可知的错误(例如一个显然不可达的目的地),记录对端的IP地址和端口号(取自传递给connect的套接字地址结构),然后立即返回到调用进程。

对于已连接UDP套接字(调用connect后),与默认的未连接UDP套接字相比,发生了三个变化:

  1. 我们再也不能给输出操作指定目的IP地址和端口号。也就是说,我们不使用sento,而改用write或send。写到已连接UDP套接字上的任何内容都自动发送到由connect指定的协议地址(如IP地址和端口号)。

     其实我们可以给已连接UDP套接字调用sendto,但是不能指定目的地址。sendto的第五个参数(指向指明目的地址的套接字地址结构的指针)必须为空指针,第六个参数(该套接字地址结构的大小)应该为0。POSIX规范指出当第五个参数为空指针时,第六个参数的取值就不再考虑。
  2. 我们不必使用recvfrom以获悉数据报的发送者,而改用read、recv或recvmsg。在一个已连接UDP套接字上,由内核为输入操作返回的数据报只有那些来自connect所指定协议地址的数据报。目的地为这个已连接UDP套接字的本地协议地址(如IP地址和端口号),发源地却不是该套接字早先connect到的协议地址的数据报,不会投递到该套接字。这样就限制一个已连接UDP套接字能且仅能与一个对端交换数据报。

     确切地说,一个已连接UDP套接字仅仅与一个IP地址交换数据报,因为connect到多播或广播地址是可能的。
  3. 由已连接UDP套接字引发的异步错误会返回给它们所在的进程,而未连接UDP套接字不接收任何异步错误。

应用进程首先调用connect指定对端的IP地址和端口号,然后使用read和write与对端进程交换数据。

来自任何其他IP地址或端口的数据报(上图中的"???"表示)不投递给这个已连接套接字,因为它们要么源IP地址要么源UDP端口不与该套接字connect到的协议地址相匹配。这些数据报可能投递给同一个主机上的其他某个UDP套接字。如果没有相匹配的其他套接字,UDP将丢弃它们并生成相应的ICMP端口不可达错误。

给一个UDP套接字多次调用connect

拥有一个已连接UDP套接字的进程可出于下列两个目的之一再次调用connect:

  • 指定新的IP地址和端口号
  • 断开套接字

第一个目的(即给一个已连接UDP套接字指定新的对端)不同于TCP套接字中connect的使用:对于TCP套接字,connect只能调用一次。

为了断开一个已连接UDP套接字,我们再次调用connect时把套接字地址结构的地址族成员(对于IPv4为sin_family,对于IPv6为sin6_family)设置为AF_UNSPEC。这么做可能会返回一个EAFNOSUPPORT错误,不过没有关系。使套接字断开连接的是在已连接UDP套接字上调用connect的进程。

性能

当应用进程在一个为连接的UDP套接字上调用sendto时,源自Berkeley的内核暂时连接该套接字,发送数据报,然后断开该连接。在一个未连接UDP套接字上给两个数据报调用sendto函数于是涉及内核执行下列6个步骤:

  1. 连接套接字

  2. 输出第一个数据报

  3. 断开套接字

  4. 连接套接字

  5. 输出第二个数据报

  6. 断开套接字连接

     另一个考虑是搜索路由表的次数。第一次临时连接需为目的IP地址搜索路由表并高速缓存这条信息。第二次临时连接注意到目的地址等于已高速缓存的路由表信息的目的地(我们假设这两个sendto调用相同的目的地址),于是就不必再次查找路由表。

当应用进程知道自己要给同一目的地址发送多个数据报时,显示连接套接字的效率更高。调用connect后调用两次write涉及内核执行以下步骤:

  1. 连接套接字
  2. 输出第一个数据报
  3. 输出第二个数据报

在这种情况下,内核只复制一次含有目的IP地址和端口号的套接字地址结构,相反当调用sendto时,需要赋值两次。

基本UDP套接字编程的更多相关文章

  1. 探索UDP套接字编程

    UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务, ...

  2. 【Python网络编程】利用Python进行TCP、UDP套接字编程

    之前实现了Java版本的TCP和UDP套接字编程的例子,于是决定结合Python的学习做一个Python版本的套接字编程实验. 流程如下: 1.一台客户机从其标准输入(键盘)读入一行字符,并通过其套接 ...

  3. 【转】 探索UDP套接字编程

    UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务, ...

  4. JavaTCP和UDP套接字编程

    在我们刚开始入门Java后端的时候可能你会觉得有点复杂,包含了很多杂七杂八的知识,例如文件上传下载,监听器,JDBC,请求重定向,请求转发等等(当然也没有很多),但是我们自己真正的去开发一个小型网站( ...

  5. 计算机网络实验 UDP套接字编程

    这是个傻瓜式操作教程 西科大计算机网络实验 UDP套接字编程 我用自己的Ubuntu16.04来举例,实验室的是虚拟机,差不多 只针对第三个题目,修改服务器来通过响应客户端发送的GetTime并发送给 ...

  6. UDP套接字编程 返回系统时间

    计算机网络实验 简单UDP套接字编程 这是学校老师自己改进了一点的题目.我预习了好久才搞明白,同学来问的时候,一大堆简单问题实在是不想回答...所以,这时候我觉得博客是个好东西! 我的任务是做客户端和 ...

  7. 【Unix网络编程】chapter8基本UDP套接字编程

    chapter8基本UDP套接字编程 8.1 概述 典型的UDP客户端/服务端的函数调用 8.2 recvfrom和sendto函数 #include <sys/socket.h> ssi ...

  8. TCP和UDP套接字编程 (java实现)

    在了解网络编程之前,我们先了解一下什么叫套接字 套接字即指同一台主机内应用层和运输层之间的接口 由于这个套接字是建立在网络上建立网络应用的可编程接口 因此也将套接字称为应用程序和网络之间的应用程序编程 ...

  9. 《Unix 网络编程》08:基本UDP套接字编程

    基本UDP套接字编程 系列文章导航:<Unix 网络编程>笔记 UDP 概述 流程图 recvfrom 和 sendto #include <sys/socket.h> ssi ...

  10. java基础----->TCP和UDP套接字编程

    这里简单的总结一下TCP和UDP编程的写法,另外涉及到HttpUrlConnection的用法 . TCP套接字 一.项目的流程如下说明: .客户输入一行字符,通过其套接字发送到服务器. .服务器从其 ...

随机推荐

  1. java核心技术卷1知识点

    1.comparable和comparator的区别. Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的. public inte ...

  2. 先做一个用来测试的chrome浏览器插件

    如何制作chrome插件 在项目汇报中,有同学提到了想要了解如何制作插件,特写该篇博客供大家查阅~ 一个简单的插件需要manifest.json.popup.html.popup.js.content ...

  3. 3、第一个Python程序

    现在,了解了如何启动和退出Python的交互式环境,我们就可以正式开始编写Python代码了. 在写代码之前,请千万不要用“复制”-“粘贴”把代码从页面粘贴到你自己的电脑上.写程序也讲究一个感觉,你需 ...

  4. Objective-C KVC讲解,包你看懂会用

    KVC:Key Value Coding,取其三个单词首字母浓缩而成.直白翻译过来就是键值编码,什么意思呢?简单来说,就是操作一个对象,也可以像操作字典一样,通过key来取值和赋值. 我们先创建一个H ...

  5. Sprint2-2.0

    1.开始一个新的冲刺: 起止:2016.6.1~2016.6.14 按照以下过程进行 ProductBacklog:继续向下细化 Sprint 计划会议:确定此次冲刺要完成的目标 Sprint Bac ...

  6. redis批量删除key 命令

    redis-cli -n 数据库编号 -a 密码 keys "过滤条件" | xargs redis-cli -n 数据库编号 -a 密码 del Demo: redis-cli ...

  7. Vue 爬坑之路(一)—— 使用 vue-cli 搭建项目 (增补)

    cd  指定好安装目录 vue init webpack  项目名称 执行  vue vue list  查看可应用模板 vue init webpack  +名字 项目已启动

  8. Jquery 表单提交后3s禁用

    <form action="${pageContext.servletContext.contextPath}/XXX/###" method="post" ...

  9. firewall和iptables

    防火墙有这三种方式,firewalld.iptables.ebtables,现在的centOS7使用的是firewalld. 下面是一些总结: 查看当前firewalld的状态 firewall-cm ...

  10. Multiple Instance Learning

    ///////////////////////////////////////////推荐学习组////////////////////////////// http://www.robots.ox. ...